Для начала, ссылка на старую статью в моем блоге о том, как предикат 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 IN
приведут к NULL
в этом случае) .
Рассмотрим эти данные:
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
)
вернет строку с OrderID = 1
.
Обратите внимание, что LEFT JOIN
решения, предложенные другими, далеко не являются наиболее эффективным решением.
Этот запрос:
SELECT *
FROM Orders o
LEFT JOIN
HeldOrders ho
ON ho.OrderID = o.OrderID
WHERE ho.OrderID IS NULL
будет использовать условие фильтра, которое необходимо будет оценить и отфильтровать все совпадающие строки, которые могут быть нумерацией
Метод ANTI JOIN
, используемый как IN
, так и EXISTS
, просто должен убедиться, что запись не существует один раз для каждой строки в Orders
, поэтому он устранит все Сначала возможны дубликаты:
NESTED LOOPS ANTI JOIN
и MERGE ANTI JOIN
будут просто пропускать дубликаты при оценке HeldOrders
.
- A
HASH ANTI JOIN
удалит дубликаты при построении хеш-таблицы.