不存在与不存在[重复]

Posted

技术标签:

【中文标题】不存在与不存在[重复]【英文标题】:NOT EXISTS vs NOT IN [duplicate] 【发布时间】:2013-01-23 11:49:08 【问题描述】:

我正在优化一些 SQL 查询(这可以被认为是我最近发布的问题的第 2 部分)并将一些 NOT IN 替换为 NOT EXISTS 谓词

我是否认为这样做的主要好处是,使用 NOT EXISTS 您可以获得这样的好处,即在找到单个匹配项时语句将终止,但 NOT IN 与计数子查询将不得不做一个完整的表扫描?

如果选择的数据包含 NULL,NOT IN 似乎也需要额外的工作,这是正确的吗?

在我在 proc 中实现它们之前,我需要确保在这两种情况下第二个语句优于第一个语句(并且在功能上等效):

案例一:

        --exclude sessions that were tracked as part of a conversion during the last response_time minutes
        -- AND session_id NOT IN (SELECT DISTINCT tracked_session_id    
        --                              FROM data.conversions WITH (NOLOCK)
        --                              WHERE client_id = @client_id
        --                              AND utc_date_completed >= DATEADD(minute, (-2) * cy.response_time, @date)
        --                              AND utc_date_completed <= @date     
        --                              AND utc_date_clicked <= @date)

        AND NOT EXISTS (SELECT 1
                            FROM data.conversions WITH (NOLOCK)
                            WHERE client_id = @client_id
                            AND utc_date_completed >= DATEADD(minute, (-2) * cy.response_time, @date)
                            AND utc_date_completed <= @date
                            AND utc_date_clicked <= @date
                            AND data.conversions.tracked_session_id = d.session_id
        )

案例 2:

        -- NOT EXISTS vs full table scan with COUNT(dashboard_id)                                   
        -- AND (SELECT COUNT(dashboard_id)
        --          FROM data.dashboard_responses WITH(NOLOCK)
        --          WHERE session_id = d.session_id
        --          AND cycle_id = cy.id
        --          AND client_id = @client_id) = 0

        AND NOT EXISTS(SELECT 1
                            FROM data.dashboard_responses
                            WHERE session_id = d.session_id
                            AND cycle_id = cy.id
                            AND client_id = @client_id)

干杯

【问题讨论】:

执行计划没有告诉你NOT IN是否执行表扫描吗?我会亲自查看计划中关于性能/IO 统计的内容。 不幸的是,我不能(轻松)对数据源运行这些存储过程以获得查询计划 从 Oracle 10g 开始 IN/NOT 显式转换为 EXISTS/NOT。 NOT IN 与 NOT EXISTS 一样有效。但如果您的子查询可能返回 NULLS,请不要使用 NOT IN。 【参考方案1】:

正如你所说的那样,两者是不同的东西。如果不为IN 的项目的子查询包含NULL 则不会返回任何结果,因为没有任何内容等于NULL 并且没有任何内容不等于NULL(甚至不是NULL)。

假设您使用两者来获得相同的结果,只要您在IN 语句中处理NULL 值,两者之间就没有区别。优化器足够聪明,知道在消除 NULL 值或使用不可为空的列后两者是相同的,因此请使用相同的 ANTI SEMI JOIN

考虑这两个表:

CREATE TABLE T (ID INT NOT NULL PRIMARY KEY);
CREATE TABLE T2 (ID INT NOT NULL PRIMARY KEY);

这两个查询得到完全相同的执行计划:

SELECT  *
FROM    T
WHERE   ID NOT IN (SELECT ID FROM T2);

SELECT  *
FROM    T
WHERE   NOT EXISTS (SELECT ID FROM T2 WHERE T.ID = T2.ID);

因为优化器知道 T2.ID 是不可为空的列。第三张桌子:

CREATE TABLE T3 (ID INT);

在 ID 列既不是索引也不是可为空的情况下,这两个查询呈现非常不同的执行计划:

SELECT  *
FROM    T
WHERE   ID NOT IN (SELECT ID FROM T3);

SELECT  *
FROM    T
WHERE   NOT EXISTS (SELECT ID FROM T3 WHERE T.ID = T3.ID);

and NOT EXISTS 会更有效率。然而,这两个再次产生(基本上)相同的执行计划:

SELECT  *
FROM    T
WHERE   ID NOT IN (SELECT ID FROM T3 WHERE T3.ID IS NOT NULL);

SELECT  *
FROM    T
WHERE   NOT EXISTS (SELECT ID FROM T3 WHERE T.ID = T3.ID);

所有这些查询和示例数据都在SQL Fiddle

编辑

真正回答你的问题:

案例 1NOT INNOT EXISTS 的性能相同,如果 tracked_session_iddata.conversions 中的不可为空的列,或者您在 In 语句中添加 WHERE tracked_Session_id IS NOT NULL。如果该列不可为空并且您不排除空值,则性能将不一样,并且假设没有空值NOT EXISTS 的性能会更好,如果没有空值,结果将不一样所以性能没有可比性。

案例 2 样本数据实际上让我感到惊讶,我认为这不会被优化为 ANTI SEMI JOIN,并且已经写了一个答案,但就在保存编辑之前我想我最好检查一下,然后惊讶地发现:

SELECT  *
FROM    T
WHERE   (   SELECT  COUNT(*) 
            FROM    T3
            WHERE   T.ID = T3.ID
        ) = 0;

优化与NOT EXISTS 完全相同。所以看起来优化器比我想象的更聪明,如果你希望计数不是 0,它只会生成不同的计划。

SQL Fiddle for Case 2

【讨论】:

在 mssql 2005 中,他们添加了对 count(*) = 0count(*) &gt; 0 的优化,因为许多人将这种语法用于 IF 语句。尽管存在是更好的选择,因为它在技术上是正确的。 blogs.technet.com/b/wardpond/archive/2007/08/27/…【参考方案2】:

你说得对,空值有很大的不同。 NOT IN 查询检查每个元素是否确实不匹配。与 null 的比较不会产生明确的结果。因此,如果您的子查询包含 null,则不会将任何内容视为“NOT IN”。

See this SQL Fiddle example.

这种行为不直观的副作用是NOT IN 实际上并不是IN 的反面。

NOT EXISTS 查询没有这个问题。

对于哪个性能更好的笼统陈述,我会犹豫不决,因为这通常取决于发生的优化类型。这就是为什么如果您关心性能,能够找出执行计划很重要。

【讨论】:

以上是关于不存在与不存在[重复]的主要内容,如果未能解决你的问题,请参考以下文章

优化存在与不存在

graphql-java-tools:空参数与不存在参数

如何在将include_tasks与不存在的目录一起使用时禁止显示警告?

Python基础(可变与不可变类型 / is和== / 特殊属性)

可变数据类型与不可变数据类型

redistemplate.delete不存在的key