不在,不存在

哪些查询更快?

不存在:

SELECT ProductID, ProductName FROM Northwind..Products p WHERE NOT EXISTS ( SELECT 1 FROM Northwind..[Order Details] od WHERE p.ProductId = od.ProductId) 

或者不在:

 SELECT ProductID, ProductName FROM Northwind..Products p WHERE p.ProductID NOT IN ( SELECT ProductID FROM Northwind..[Order Details]) 

查询执行计划说,他们都做同样的事情。 如果是这样,那么推荐的forms是什么?

这是基于NorthWind数据库。

[编辑]

刚刚发现这个有用的文章: http : //weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx

我想我会坚持不存在。

我总是默认NOT EXISTS

执行计划目前可能是相同的,但如果任一列在将来被更改为允许NULLNOT IN版本将需要做更多的工作(即使数据中实际上不存在NULL ),以及如果NULL存在, NOT IN ,不可能是你想要的。

Products.ProductID[Order Details].ProductID不允许NULLNOT IN将被视为与以下查询相同。

 SELECT ProductID, ProductName FROM Products p WHERE NOT EXISTS (SELECT * FROM [Order Details] od WHERE p.ProductId = od.ProductId) 

确切的计划可能会有所不同,但对于我的示例数据,我得到以下。

既不NULL也不

一个相当普遍的错误观念似乎是,与连接相比,相关的子查询总是“坏”的。 他们当然可以是当他们强制嵌套循环计划(子查询逐行评估),但是这个计划包括反半连接逻辑运算符。 反半连接不限于嵌套循环,但可以使用散列或合并(如本例中)连接。

 /*Not valid syntax but better reflects the plan*/ SELECT p.ProductID, p.ProductName FROM Products p LEFT ANTI SEMI JOIN [Order Details] od ON p.ProductId = od.ProductId 

如果[Order Details].ProductIDNULL那么查询就变成了

 SELECT ProductID, ProductName FROM Products p WHERE NOT EXISTS (SELECT * FROM [Order Details] od WHERE p.ProductId = od.ProductId) AND NOT EXISTS (SELECT * FROM [Order Details] WHERE ProductId IS NULL) 

其原因是,如果[Order Details]包含任何NULL ProductId ,则正确的语义是不返回结果。 请参阅额外的反半连接和行计数后台来validation添加到计划中的这一点。

一个NULL

如果Products.ProductID也更改为NULL则查询将变为

 SELECT ProductID, ProductName FROM Products p WHERE NOT EXISTS (SELECT * FROM [Order Details] od WHERE p.ProductId = od.ProductId) AND NOT EXISTS (SELECT * FROM [Order Details] WHERE ProductId IS NULL) AND NOT EXISTS (SELECT * FROM (SELECT TOP 1 * FROM [Order Details]) S WHERE p.ProductID IS NULL) 

之所以这样做,是因为NULL Products.ProductId不应该在结果中返回, 除非 NOT IN子查询根本没有返回任何结果(即[Order Details]表是空的)。 在这种情况下,它应该。 在我的样本数据计划中,通过添加另一个反半连接来实现。

两者都是NULL

这一点的影响显示在Buckley已经联系的博客文章中 。 在这个例子中,逻辑读取的数量从大约400增加到500,000。

此外,单个NULL可以将行数减less到零的事实使得基数估计非常困难。 如果SQL Server假定会发生这种情况,但实际上数据中没有NULL行,那么执行计划的其余部分可能会更糟糕,如果这只是更大的查询的一部分,那么使用不恰当的嵌套循环会导致重复执行昂贵子树例如 。

但是,这不是在NULL -able列上的NOT IN唯一可能的执行计划。 本文展示了另一个针对AdventureWorks2008数据库的查询。

对于NOT NULL列中的NOT IN或者对于可为空或不可空列的NOT EXISTS ,它给出以下计划。

不存在

当列更改为NULL时, NOT IN计划现在看起来像

不在 - 空

它在计划中增加了一个额外的内部连接操作符。 这个装置在这里解释 。 将Sales.SalesOrderDetail.ProductID = <correlated_product_id>上的单个相关索引查找转换为每个外部行的两个查找。 额外的一个是WHERE Sales.SalesOrderDetail.ProductID IS NULL

由于这是在反半联接之下,如果那个返回任何行,第二个seek不会发生。 但是,如果Sales.SalesOrderDetail不包含任何NULL ProductID则会使所需的查找操作数增加一倍。

另外请注意,NOT IN不等同于NOT EXISTS。

这篇文章解释得非常好

http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/

当子查询返回一个null时,NOT IN将不匹配任何行。

其原因可以通过查看NOT IN操作实际意义的细节来find。

比方说,为了说明目的,表中有4行叫做t,有一个名为ID的列,其值为1..4

 WHERE SomeValue NOT IN (SELECT AVal FROM t) 

相当于

 WHERE SomeValue != (SELECT AVal FROM t WHERE ID=1) AND SomeValue != (SELECT AVal FROM t WHERE ID=2) AND SomeValue != (SELECT AVal FROM t WHERE ID=3) AND SomeValue != (SELECT AVal FROM t WHERE ID=4) 

让我们进一步说AVal是NULL,其中ID = 4.因此,!=比较返回UNKNOWN。 AND的逻辑真值表表明UNKNOWN和TRUE是UNKNOWN,UNKNOWN和FALSE是FALSE。 没有值可以与UNKNOWN AND'd来产生结果TRUE

因此,如果该子查询的任何一行返回NULL,则整个NOT IN运算符将计算为FALSE或NULL,并且不会返回任何logging

如果执行计划员说他们是一样的,他们是一样的。 使用哪一个会使你的意图更明显 – 在这种情况下,第二个。

其实,我相信这将是最快的:

 SELECT ProductID, ProductName FROM Northwind..Products p outer join Northwind..[Order Details] od on p.ProductId = od.ProductId) WHERE od.ProductId is null 

我有一个约有12万条logging的表,只需要select那些不存在(与一个varchar列匹配)在其他四个表中约1500,4000,400,200,200行。所有涉及表具有唯一索引在有关的Varchar列上。

NOT IN花费大约10分钟, NOT EXISTS花了4秒。

我有一个recursion查询,可能有一些未被激活的部分,可能有助于10分钟,但另一种select4秒解释,至less对我来说, NOT EXISTS是好得多,或至less, INEXISTS不完全相同在继续使用代码之前总是值得一试。

在你的具体例子中,它们是相同的,因为优化器已经找出了你正在做的事情在两个例子中都是一样的。 但有可能的是,在非平凡的例子中,优化器可能不会这样做,并且在这种情况下,有时有理由更喜欢另一个。

如果您要在外部select中testing多个行,则不要select“首选”。 可以在执行开始时计算NOT IN语句内的子查询,并可以根据外部select中的每个值检查临时表,而不是每次都重新运行子查询,这与NOT EXISTS语句所要求的一样。

如果子查询必须与外部select相关联,那么NOT EXISTS可能是优选的,因为优化器可能发现简化,从而防止创build任何临时表来执行相同的function。

我正在使用

 SELECT * from TABLE1 WHERE Col1 NOT IN (SELECT Col1 FROM TABLE2) 

并发现它给出了错误的结果(错误我的意思是没有结果)。 由于TABLE2.Col1中有一个NULL。

将查询更改为

 SELECT * from TABLE1 T1 WHERE NOT EXISTS (SELECT Col1 FROM TABLE2 T2 WHERE T1.Col1 = T2.Col2) 

给了我正确的结果。

从那以后,我开始在每个地方使用NOT EXISTS。

这取决于..

 SELECT x.col FROM big_table x WHERE x.key IN( SELECT key FROM really_big_table ); 

不会比较慢,限制什么查询检查是否他们的钥匙是在很大的尺寸。在这种情况下EXISTS将是可取的。

但是,根据DBMS的优化器,这可能没有什么不同。

作为EXISTS更好的一个例子

 SELECT x.col FROM big_table x WHERE EXISTS( SELECT key FROM really_big_table WHERE key = x.key); AND id = very_limiting_criteria 

如果优化器说他们是相同的,那么考虑人为因素。 我更喜欢看不存在:)

    Interesting Posts