哪些查询更快?

不存在:

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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top