不存在与不存在[重复]
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
编辑
真正回答你的问题:
案例 1 与 NOT IN
或 NOT EXISTS
的性能相同,如果 tracked_session_id
是 data.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(*) = 0
和 count(*) > 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
查询没有这个问题。
对于哪个性能更好的笼统陈述,我会犹豫不决,因为这通常取决于发生的优化类型。这就是为什么如果您关心性能,能够找出执行计划很重要。
【讨论】:
以上是关于不存在与不存在[重复]的主要内容,如果未能解决你的问题,请参考以下文章
如何在将include_tasks与不存在的目录一起使用时禁止显示警告?