2

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

我是否认为这样做的主要好处是,使用 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
        )

案例二:

        -- 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)

干杯

4

2 回答 2

5

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

假设您使用两者来实现相同的结果,只要您在语句中处理NULL值,两者之间就没有区别。IN优化器足够聪明,可以知道在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);

而 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 INor NOT EXISTSif tracked_session_idis an non nullable column indata.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;

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

案例 2 的 SQL 小提琴

于 2013-01-23T12:08:40.203 回答
2

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

请参阅此 SQL Fiddle 示例。

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

NOT EXISTS查询没有这个问题。

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

于 2013-01-23T11:59:46.117 回答