这些查询中哪一个更快? php
不存在: html
SELECT ProductID, ProductName FROM Northwind..Products p WHERE NOT EXISTS ( SELECT 1 FROM Northwind..[Order Details] od WHERE p.ProductId = od.ProductId)
或不在: web
SELECT ProductID, ProductName FROM Northwind..Products p WHERE p.ProductID NOT IN ( SELECT ProductID FROM Northwind..[Order Details])
查询执行计划说他们都作一样的事情。 若是是这样,建议使用哪一种形式? sql
这基于NorthWind数据库。 数据库
[编辑] 并发
刚刚发现这篇有用的文章: http : //weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx ide
我想我会坚持不存在。 优化
还应注意,当为空时,NOT IN不等于NOT EXISTS。 spa
这篇文章很好地解释了 code
http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/
当子查询甚至返回一个null时,NOT IN将不匹配任何行。
能够经过查看NOT IN操做实际含义的详细信息来找到其缘由。
假设出于说明目的,表中有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进行与运算以产生结果TRUE
所以,若是该子查询的任何行返回NULL,则整个NOT IN运算符将求值为FALSE或NULL,而且将不返回任何记录
我老是默认为NOT EXISTS
。
目前的执行计划可能相同,可是若是未来更改任一列以容许NULL
则NOT IN
版本将须要作更多的工做(即便数据中实际上没有NULL
),而且不管如何,若是存在NULL
则 NOT IN
不太多是您想要的。
当Products.ProductID
或[Order Details].ProductID
不容许NULL
将NOT IN
与如下查询等同对待。
SELECT ProductID, ProductName FROM Products p WHERE NOT EXISTS (SELECT * FROM [Order Details] od WHERE p.ProductId = od.ProductId)
确切的计划可能会有所不一样,可是对于个人示例数据,我获得如下信息。
一个合理的广泛误解彷佛是与联接相比,相关的子查询老是“很差的”。 当他们强制执行嵌套循环计划(逐行评估子查询)时,固然能够,可是该计划包括反半联接逻辑运算符。 反半联接不限于嵌套循环,还可使用哈希或合并(如本例所示)联接。
/*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].ProductID
是NULL
-able查询就变成
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
,则正确的语义将不返回任何结果。 请参阅额外的反半链接和行计数假脱机以验证是否已将其添加到计划中。
若是Products.ProductID
也更改成NULL
-able,则查询变为
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)
这样作的缘由是, 除非 NOT IN
子查询根本不返回任何结果(即[Order Details]
表为空), 不然不该在结果中返回NULL
Products.ProductId
。 在这种状况下应该。 在个人样本数据计划中,这是经过添加另外一个反半联接来实现的,以下所示。
Buckley已经连接的博客文章中显示了这种效果。 在该示例中,逻辑读取的数量从大约400增长到500,000。
另外,单个NULL
能够将行计数减小到零这一事实使基数估计很是困难。 若是SQL Server假定会发生这种状况,但实际上数据中没有NULL
行,则执行计划的其他部分可能会灾难性地恶化,若是这只是较大查询的一部分, 而且嵌套循环不当会致使重复执行昂贵的查询例如子树 。
可是,这不是可为NULL
列上的NOT IN
惟一可行的执行计划。 本文显示了另外一个针对AdventureWorks2008
数据库的查询。
对于NOT NULL
列上的NOT IN
或针对可为空或不可为空的列的NOT EXISTS
,它给出了如下计划。
当列更改成NULL
-able时, NOT IN
计划如今看起来像
它将额外的内部联接运算符添加到计划中。 这里说明该装置。 只须要将Sales.SalesOrderDetail.ProductID = <correlated_product_id>
上的单个相关索引搜索转换为每一个Sales.SalesOrderDetail.ProductID = <correlated_product_id>
两个搜索。 另外一个在WHERE Sales.SalesOrderDetail.ProductID IS NULL
。
因为这是在反半联接下,若是该联接返回任何行,则不会发生第二次寻道。 可是,若是Sales.SalesOrderDetail
不包含任何NULL
ProductID
它将使所需的查找操做次数增长一倍。
我在用
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