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)
干杯
正如你所说,这两者是不同的东西.如果要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);
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上
编辑
要真正回答你的问题:
情况1与NOT 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,它只会生成一个不同的计划.