使用“not in”子查询编写select语句的最有效方法是什么?

时间:2009-08-31 15:32:17

标签: sql sql-server tsql

编写类似于下面的select语句的最有效方法是什么。

SELECT *
FROM Orders
WHERE Orders.Order_ID not in (Select Order_ID FROM HeldOrders)

要点是,当项目不在另一个表格中时,您需要来自一个表格的记录。

5 个答案:

答案 0 :(得分:20)

对于初学者,我的博客中有关NOT IN谓词在SQL Server(以及其他系统)中如何运作的旧文章的链接:


您可以按如下方式重写它:

SELECT  *
FROM    Orders o
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    HeldOrders ho
        WHERE   ho.OrderID = o.OrderID
        )
但是,

,大多数数据库都会对这些查询进行相同的处理。

这两个查询都会使用某种ANTI JOIN

如果您要检查两列或更多列,这对SQL Server非常有用,因为SQL Server不支持此语法:

SELECT  *
FROM    Orders o
WHERE   (col1, col2) NOT IN
        (
        SELECT  col1, col2
        FROM    HeldOrders ho
        )

但请注意,由于NOT IN处理NULL值的方式,Held.Orders可能会非常棘手。

如果NULL可以为空,但未找到任何记录且子查询只返回一个IN,则整个查询将不返回任何内容(NOT INNULL都将评估在这种情况下为Orders: OrderID --- 1 HeldOrders: OrderID --- 2 NULL

考虑以下数据:

SELECT  *
FROM    Orders o
WHERE   OrderID NOT IN
        (
        SELECT  OrderID
        FROM    HeldOrders ho
        )

此查询:

SELECT  *
FROM    Orders o
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    HeldOrders ho
        WHERE   ho.OrderID = o.OrderID
        )

将返回 nothing ,这可能不是您所期望的。

然而,这一个:

OrderID = 1

将返回LEFT JOIN行。

请注意,其他人提出的SELECT * FROM Orders o LEFT JOIN HeldOrders ho ON ho.OrderID = o.OrderID WHERE ho.OrderID IS NULL 解决方案远非最有效的解决方案。

此查询:

ANTI JOIN

将使用过滤条件,该条件需要评估并过滤掉所有匹配行,这些行可以是数字

INEXISTS使用的Orders方法只需要确保记录不存在一次每行{{} 1}},所以它将首先消除所有可能的重复:

  • NESTED LOOPS ANTI JOINMERGE ANTI JOIN只会在评估HeldOrders时跳过重复项。
  • 构建哈希表时,HASH ANTI JOIN将消除重复。

答案 1 :(得分:8)

“效率最高”将根据表大小,索引等而有所不同。换句话说,它会根据您使用的具体情况而有所不同。

根据具体情况,我常用三种方法来完成你想要的工作。

<强> 1。如果对Orders.order_id编制索引,并且HeldOrders相当小,那么您的示例工作正常。

<强> 2。另一种方法是“相关子查询”,它与你所拥有的内容略有不同......

SELECT *
FROM Orders o
WHERE Orders.Order_ID not in (Select Order_ID 
                              FROM HeldOrders h 
                              where h.order_id = o.order_id)

注意添加where子句。当HeldOrders具有大量行时,这往往更好地工作。 Order_ID需要在两个表中编入索引。

第3。我有时使用的另一种方法是留下外连接......

SELECT *
FROM Orders o
left outer join HeldOrders h on h.order_id = o.order_id
where h.order_id is null

当使用左外连接时,当匹配行时,h.order_id将在其中匹配o.order_id。如果没有匹配的行,则h.order_id将为NULL。通过检查where子句中的NULL值,您可以过滤掉没有匹配项的所有内容。

在各种情况下,这些变体中的每一种都可以或多或少地有效。

答案 2 :(得分:4)

您可以使用LEFT OUTER JOIN并在右侧表格中查看NULL

SELECT O1.*
FROM Orders O1
LEFT OUTER JOIN HeldOrders O2
ON O1.Order_ID = O2.Order_Id
WHERE O2.Order_Id IS NULL

答案 3 :(得分:1)

我不确定什么是最有效的,但其他选项是:

1. Use EXISTS

SELECT * 
FROM ORDERS O 
WHERE NOT EXISTS (SELECT 1 
                  FROM HeldOrders HO 
                  WHERE O.Order_ID = HO.OrderID)

2. Use EXCEPT

SELECT O.Order_ID 
FROM ORDERS O 
EXCEPT 
SELECT HO.Order_ID 
FROM HeldOrders

答案 4 :(得分:0)

尝试

SELECT *
FROM Orders
LEFT JOIN HeldOrders
ON HeldOrders.Order_ID = Orders.Order_ID
WHERE HeldOrders.Order_ID IS NULL