题
哪些查询更快?
不存在:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE NOT EXISTS (
SELECT 1
FROM Northwind..[Order Details] od
WHERE p.ProductId = od.ProductId)
或不是IN:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE p.ProductID NOT IN (
SELECT ProductID
FROM Northwind..[Order Details])
查询执行计划表明他们都做同样的事情。如果是这种情况,这是推荐的形式?
这是基于NorthWind数据库。
[编辑]
刚刚找到这篇有用的文章: http://weblogs.sqlteam.com/mladenp/archive/2007 /05/18/60210.aspx
我想我会坚持使用NOT EXISTS。
解决方案
我总是默认为NOT EXISTS
。
目前执行计划可能相同,但如果将来某个列被更改以允许NULL
s NOT IN
版本需要做更多工作(即使实际上没有Products.ProductID
s)在数据中)和[Order Details].ProductID
如果 存在的语义不太可能是你想要的那些。
当[Order Details]
或ProductId
不允许Products.ProductId
时,AdventureWorks2008
将被视为与以下查询完全相同。
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
如果NOT NULL
Sales.SalesOrderDetail.ProductID = <correlated_product_id>
- 则查询成为
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)
原因是如果WHERE Sales.SalesOrderDetail.ProductID IS NULL
包含任何Sales.SalesOrderDetail
ProductID
s,则正确的语义是不返回任何结果。请参阅额外的反半连接和行计数假脱机以验证添加到计划中的内容。
如果<=>也被更改为<=> - 那么查询就变为
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)
之所以这样做是因为<=> <=>不应该在结果中返回,如果<=>子查询根本不返回任何结果(即< =>表格为空)。它应该在哪种情况下。在我的样本数据计划中,这是通过添加另一个反半连接来实现的。
这种效果显示在已经由Buckley链接的博客文章。在该示例中,逻辑读取的数量从大约400增加到500,000。
此外,单个<=>可以将行数减少到零的事实使得基数估计非常困难。如果SQL Server认为会发生这种情况,但实际上数据中没有<=>行,那么执行计划的其余部分可能会更糟糕,如果这只是更大查询的一部分,不恰当的嵌套循环导致重复执行昂贵的子树,例如。
但这不是<=> - < - >列上唯一可能的<=>执行计划。 这篇文章展示另一篇文章针对<=>数据库的查询。
对于<=>列上的<=>或<=>对可空或不可为空的列,它给出了以下计划。
当列更改为<=>时,<=>计划现在看起来像
它为计划添加了一个额外的内连接运算符。此设备此处说明。只需将<=>上的单个相关索引搜索转换为每个外行两个搜索。另外一个是<=>。
因为这是一个反半连接,如果那个返回任何行,第二次搜索将不会发生。但是,如果<=>不包含任何<=> <=> s,它将使所需的搜索操作数量翻倍。
其他提示
还要注意,当涉及到null时,NOT IN不等同于NOT EXISTS。
这篇文章很好地解释了
http://sqlinthewild.co.za/的index.php / 2010/02/18 /未存在-VS-不费/
当子查询返回一个null时,NOT IN将不匹配任何 行。
通过查看详细信息可以找到原因 NOT IN operation实际上意味着。
让<!>#8217; s说,为了说明的目的,有4行 表名为t,<!>#8217;是一个名为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)
让<!>#8217;进一步说AVal是NULL,其中ID = 4.因此,!= 比较返回UNKNOWN。 AND状态的逻辑真值表 UNKNOWN和TRUE是UNKNOWN,UNKNOWN和FALSE是FALSE。有 没有值可以AND <!>#8217; d使用UNKNOWN生成结果TRUE
因此,如果该子查询的任何行返回NULL,则整个NOT IN 运算符将评估为FALSE或NULL,并且不会记录任何记录 返回
如果执行计划员说他们是相同的,那么他们就是一样的。使用任何一个会使你的意图更明显 - 在这种情况下,第二个。
实际上,我相信这将是最快的:
SELECT ProductID, ProductName
FROM Northwind..Products p
outer join Northwind..[Order Details] od on p.ProductId = od.ProductId)
WHERE od.ProductId is null
我有一个包含大约120,000条记录的表,需要在其他四个表中选择那些不存在的记录(与varchar列匹配),行数约为1500,4000,40000,200。所有涉及的表在相关的Varchar
列上有唯一索引。
NOT IN
大约需要10分钟,NOT EXISTS
需要4秒钟。
我有一个递归查询,可能有一些未调整的部分可能有助于10分钟,但另一个选项花了4秒解释,至少对我来说IN
更好或至少那个EXISTS
和<=>不完全相同,在开始使用代码之前总是值得检查。
在您的具体示例中它们是相同的,因为优化器已经弄清楚您尝试做的是两个示例中的相同内容。但有可能的是,在非平凡的例子中,优化器可能不会这样做,并且在这种情况下,有理由有时会优先选择其中一个。
如果要在外部选择中测试多行,则应首选 NOT IN
。可以在执行开始时评估NOT EXISTS
语句中的子查询,并且可以针对外部选择中的每个值检查临时表,而不是每次都重新运行子选择,如<=所需的那样。 >声明。
如果子查询必须与外部选择相关联,则<=>可能更可取,因为优化器可能会发现一种简化,可以防止创建任何临时表来执行相同的功能。
我正在使用
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。
它们非常相似,但并不完全相同。
就效率而言,我发现左连接为空语句更有效(当选择丰富的行时)
如果优化器说它们是相同的,那么考虑人为因素。我更愿意看到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