不存在而不是存在

man*_*p84 2 sql sql-server optimization plsql

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

我是否正确地认为这样做的主要好处是,使用NOT EXISTS可以获得声明在找到单个匹配时终止的好处,但是对于计数子查询而言,NOT IN将需要进行全表扫描?

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

在我在proc中实现它们之前,我需要确保第二个语句比这两个案例中的第一个语句(和功能上等效的)更好:

情况1:

        --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
        )
Run Code Online (Sandbox Code Playgroud)

案例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)
Run Code Online (Sandbox Code Playgroud)

干杯

Gar*_*thD 5

正如你所说,这两者是不同的东西.如果要IN包含的项的子查询不包含NULL任何结果将返回,因为没有任何等于NULL且没有任何不相等NULL(甚至不为NULL).

假设您使用两者来实现相同的结果,只要您NULLIN语句中处理值,两者之间就没有区别.优化器足够聪明,知道NULL消除了值,或者使用不可为空的列,两者是相同的,所以使用相同的ANTI SEMI JOIN.

考虑这两个表:

CREATE TABLE T (ID INT NOT NULL PRIMARY KEY);
CREATE TABLE T2 (ID INT NOT NULL PRIMARY KEY);
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

因为优化器知道T2.ID是一个不可为空的列.第三个表:

CREATE TABLE T3 (ID INT);
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

而不是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);
Run Code Online (Sandbox Code Playgroud)

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

编辑

要真正回答你的问题:

情况1NOT IN或者NOT EXISTS是否tracked_session_id为非可空列的性能相同data.conversions,或者您WHERE tracked_Session_id IS NOT NULL在In语句中添加.如果列不可为空并且您不排除空值,则性能将不相同,并且假设没有空值NOT EXISTS将表现更好,如果没有空值,结果将不会相同,因此性能是没有可比性.

案例2实际上让我对样本数据感到惊讶,我曾假设这不会被优化成一个ANTI SEMI JOIN,并且已经写了一个答案,但是在保存编辑之前我认为我最好检查一下,并且很惊讶地看到这个:

SELECT  *
FROM    T
WHERE   (   SELECT  COUNT(*) 
            FROM    T3
            WHERE   T.ID = T3.ID
        ) = 0;
Run Code Online (Sandbox Code Playgroud)

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

案例2的SQL小提琴