НЕ следует избегать IN?

14

Среди некоторых разработчиков SQL Server широко распространено мнение, что NOT INоно ужасно медленное , и запросы должны быть переписаны так, чтобы они возвращали тот же результат, но не использовали «злые» ключевые слова. ( пример ).

Есть ли в этом правда?

Например, существует ли какая-либо известная ошибка в SQL Server (какая версия?), Из-за которой запросы, использующие запросы, NOT INимеют худший план выполнения, чем эквивалентный запрос, использующий

  • в LEFT JOINсочетании с NULLчеком или
  • (SELECT COUNT(*) ...) = 0в WHEREпункте?
Heinzi
источник
7
Эта статья очень неточная, хотя. «In» не «должен запускать один и тот же запрос снова и снова для каждой строки в TableOne». Кажется, что создатель этого поста полагает, что IN/ NOT INвсегда будет реализован с помощью вложенных циклов. И я понятия не имею, что stops SQL Server from creating a ‘plan’должно означать.
Мартин Смит
5
@ Heinzi Эта статья, на которую вы ссылаетесь, должна умереть в огне, она полна чепухи. Например: «Чтобы заменить IN, мы используем INNER JOIN. Они фактически одно и то же». Проблема в том, что они не одно и то же. Я бы не стал доверять тому, кто не знает базового SQL, то есть разницы между соединением и полусоединением, для анализа чего-либо о поведении SQL-сервера.
ypercubeᵀᴹ

Ответы:

14

Я не думаю, что это имеет какое-то отношение к тому, чтобы быть ужасно медленным; это имеет отношение к тому, чтобы быть потенциально неточным. Например, с учетом следующих данных - заказов, которые могут быть размещены либо отдельным клиентом, либо партнером B2B:

DECLARE @Customers TABLE(CustomerID INT);

INSERT @Customers VALUES(1),(2);

DECLARE @Orders TABLE(OrderID INT, CustomerID INT, CompanyID INT);

INSERT @Orders VALUES(10,1,NULL),(11,NULL,5);

Допустим, я хочу найти всех клиентов, которые никогда не размещали заказ. Учитывая данные, есть только один: клиент № 2. Вот три способа написать запрос для поиска этой информации (есть и другие):

SELECT [NOT IN] = CustomerID FROM @Customers 
  WHERE CustomerID NOT IN (SELECT CustomerID FROM @Orders);

SELECT [NOT EXISTS] = CustomerID FROM @Customers AS c 
  WHERE NOT EXISTS (SELECT 1 FROM @Orders AS o
  WHERE o.CustomerID = c.CustomerID);

SELECT [EXCEPT] = CustomerID FROM @Customers
EXCEPT SELECT CustomerID FROM @Orders;

Результаты:

NOT IN
------
                 -- <-- no results. Is that what you expected?

NOT EXISTS
----------
2

EXCEPT
------
2

Теперь есть некоторые проблемы с производительностью, и я расскажу о них в этом посте . В зависимости от данных и индексов, NOT EXISTSкак правило, выигрывают NOT IN, и я не знаю, может ли он когда-либо работать хуже. Следует также отметить, что EXCEPTможет быть введена отдельная операция сортировки, поэтому вы можете получить разные данные (опять же, в зависимости от источника). И что популярный LEFT OUTER JOIN ... WHERE right.column IS NULLшаблон всегда худший исполнитель.

У Мартина Смита также много полезной информации в ответе на SO .

Аарон Бертран
источник