下面哪个查询更快?

不存在:

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])

查询执行计划表示它们都做相同的事情。如果是这样的话,推荐哪种形式?

这是基于北风的数据库。

(编辑)

刚找到这篇有用的文章: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx

我想我还是用“不存在”吧。


当前回答

我总是默认为不存在。

目前的执行计划可能是相同的,但如果将来任意一列被更改为允许空值,则NOT in版本将需要做更多的工作(即使数据中实际上没有空值),而且如果存在空值,则NOT in的语义不太可能是您想要的语义。

当两者都不是产品。ProductID或[订单详细信息]。该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 

如果[订单详情]。ProductID是可空的,然后查询变成

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 ProductIds,正确的语义是不返回任何结果。查看额外的反半连接和行计数线轴,以验证添加到计划中的这一点。

如果产品。ProductID也被更改为可空查询

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乘积。ProductId不应该在结果中返回,除非not in子查询根本不返回结果(即[Order Details]表为空)。在这种情况下,应该如此。在我的示例数据的计划中,这是通过添加另一个反半连接来实现的,如下所示。

Buckley已经链接了一篇博文,其中显示了这种影响。在这个例子中,逻辑读取的数量从400增加到500,000。

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

然而,对于可空列上的not IN,这并不是唯一可行的执行计划。本文展示了另一个针对AdventureWorks2008数据库的查询。

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

当列更改为可空时,NOT IN计划现在看起来像这样

它向计划中添加了一个额外的内部连接运算符。这里解释一下这个装置。这一切都是为了将之前Sales.SalesOrderDetail.ProductID = <correlated_product_id>上的单个相关索引查找转换为每个外部行两个查找。另外一个是WHERE Sales.SalesOrderDetail.ProductID is NULL。

由于这是在反半连接下,如果该连接返回任何行,则不会发生第二次寻道。然而,如果销售。SalesOrderDetail不包含任何NULL productid,它将使所需的查找操作数量增加一倍。

其他回答

在您的特定示例中,它们是相同的,因为优化器已经发现您正在尝试做的事情在两个示例中是相同的。但在非平凡的例子中,优化器可能不会这样做,在这种情况下,有时有理由更喜欢其中一个。

如果在外层选择中测试多行,则首选NOT IN。可以在执行开始时计算NOT IN语句中的子查询,并且可以根据外部选择中的每个值检查临时表,而不是像NOT EXISTS语句那样每次都需要重新运行子选择。

如果子查询必须与外部选择相关联,那么NOT EXISTS可能更可取,因为优化器可能会发现一种简化,可以防止创建任何临时表来执行相同的功能。

事实上,我相信这是最快的:

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

这取决于. .

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

不会相对较慢,也没有太多限制查询检查的大小,看看他们的键是否在。在这种情况下,存在是可取的。

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

作为一个例子,当存在更好

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

我在用

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:)