Не в vs не существует



какой из этих запросов будет быстрее?



НЕ СУЩЕСТВУЕТ:



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


план выполнения запроса говорит, что они оба делают то же самое. Если это так,то какая форма рекомендуется?



это основано на базе данных NorthWind.



[Edit]



просто нашел эту полезную статью:
http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx



Я думаю, Я буду придерживаться не существует.

506   10  

10 ответов:

я всегда по умолчанию NOT EXISTS.

планы выполнения могут быть одинаковыми на данный момент, но если какой-либо столбец будет изменен в будущем, чтобы разрешить NULLС NOT IN версия должна будет сделать больше работы (даже если нет NULL s фактически присутствуют в данных) и семантика NOT IN если NULLs are настоящее вряд ли будут те, которые вы хотите в любом случае.

когда ни Products.ProductID или [Order Details].ProductID разрешить NULLС NOT IN будем лечиться тождественно следующему запросу.

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId) 

точный план может отличаться, но для моего примера данных я получаю следующее.

Neither NULL

довольно распространенное заблуждение заключается в том, что коррелированные подзапросы всегда "плохи" по сравнению с соединениями. Они, конечно, могут быть, когда они заставляют план вложенных циклов (подзапрос оценивается строка за строкой), но этот план включает в себя логический оператор anti semi join. Анти полуобъединения не ограничиваются вложенными циклами, но также можно использовать хэш или слияние (как в этом примере).

/*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 

если [Order Details].ProductID и NULL - способный запрос тогда становится

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] содержит NULLProductIds не возвращает никаких результатов. См. дополнительную катушку Anti semi join и Row count, чтобы проверить это, добавленное в план.

One NULL

если Products.ProductID также изменяется, чтобы стать NULL-возможность запроса тогда становится

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) 

причина этого в том, что a NULLProducts.ProductId не должны быть возвращены в результатах за исключением если NOT IN sub запрос не должен был возвращать никаких результатов вообще (т. е. [Order Details] таблица пуста). В таком случае это должно быть так. В плане для моих примерных данных это реализуется путем добавления другого анти-полу-соединения, как показано ниже.

Both NULL

эффект этого показан в сообщение в блоге уже связаны Бакли. В приведенном примере количество логических операций чтения увеличивается примерно с 400 до 500 000.

дополнительно тот факт, что один NULL может уменьшить количество строк до нуля делает оценку мощности очень трудно. Если SQL Server предполагает, что это произойдет, но на самом деле не было NULL строки в данных остальная часть плана выполнения может быть катастрофически хуже, если это просто часть большего запроса,с неуместными вложенными циклами вызывая повторное выполнение дорогостоящего поддерева например.

это не единственный возможный план выполнения для A NOT IN на NULL-в состоянии однако колонке. эта статья показывает еще один на запрос от или NOT EXISTS против столбца с нулевым или ненулевым значением он дает следующий план.

Not EXists

при изменении столбца к NULL-возможность NOT IN план теперь выглядит как

Not In - Null

он добавляет дополнительный внутренний оператор соединения к плану. Этот аппарат пояснил, что. Это все там, чтобы преобразовать предыдущий один коррелированный индекс искать на Sales.SalesOrderDetail.ProductID = <correlated_product_id> до двух поисков на внешнюю строку. Дополнительный находится на WHERE Sales.SalesOrderDetail.ProductID IS NULL.

поскольку это находится под анти-полу-соединением, если это возвращает какие-либо строки, второй поиск не произойдет. Однако если Sales.SalesOrderDetail не содержат любые NULLProductIDs это удвоит количество операций поиска, необходимых.

также имейте в виду, что NOT IN не эквивалентно NOT EXISTS, когда дело доходит до null.

этот пост объясняет это очень хорошо

http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/

когда подзапрос возвращает даже один null, NOT IN не будет соответствовать ни одному строки.

причину этого можно найти, посмотрев на детали того, что Не в действии на самом деле означает.

скажем, для иллюстрации, что есть 4 строки в таблица называется t, есть столбец с именем 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)

далее скажем, что AVal равен нулю, где ID = 4. Следовательно, что != сравнение возвращает значение Unknown. Логическая таблица истинности для и состояний неизвестно, что и правда, неизвестно, неизвестно, а ложь-ложью. Есть нет значения, которое может быть AND'D с неизвестным, чтобы получить результат 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. Все вовлеченные таблицы имеют уникальный индекс на соответствующем .

NOT IN заняло около 10 минут,NOT EXISTS ушло 4 секунды.

у меня есть рекурсивный запрос, который мог бы иметь какой-то не настроенный раздел, который мог бы внести свой вклад в 10 минут, но другой вариант, занимающий 4 секунды, объясняет, по крайней мере, мне это NOT EXISTS гораздо лучше или, по крайней мере, что IN и EXISTS не совсем то же самое и всегда стоит проверить, прежде чем идти вперед с кодом.

в вашем конкретном примере они одинаковы, потому что оптимизатор выяснил, что вы пытаетесь сделать то же самое в обоих примерах. Но вполне возможно, что в нетривиальных примерах оптимизатор может этого не делать, и в этом случае есть причины Иногда предпочитать одно другому.

NOT IN должно быть предпочтительным, если вы тестируете несколько строк в ваш внешний выбор. Подзапрос внутри NOT IN оператор может быть вычислен в начале выполнения, и временная таблица может быть проверена по каждому значению во внешнем select, а не повторно запускать подвыборку каждый раз, как это требуется с NOT EXISTS заявление.

если подзапрос должны соотносится с внешним выбором, затем NOT EXISTS может быть предпочтительнее, так как оптимизатор может обнаружить упрощение, которое предотвращает создание любых временных таблиц для выполнения той же функции.

Я был с помощью

SELECT * from TABLE1 WHERE Col1 NOT IN (SELECT Col1 FROM TABLE2)

и обнаружил, что он дает неправильные результаты (под неправильным я подразумеваю отсутствие результатов). Поскольку в таблице 2 был NULL.На col1.

при изменении запроса

SELECT * from TABLE1 T1 WHERE NOT EXISTS (SELECT Col1 FROM TABLE2 T2 WHERE T1.Col1 = T2.Col2)

дал мне правильные результаты.

С тех пор я начал использовать не существует везде.

Они очень похожи, но не одинаковы.

с точки зрения эффективности, я нашел левое соединение равно null оператор более эффективный (когда необходимо выбрать обилие строк)

если оптимизатор говорит, что они одинаковы, то рассмотрим человеческий фактор. Я предпочитаю видеть не существует :)

Это зависит..

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

не было бы относительно медленным, чтобы не сильно ограничивать размер того, что проверяет запрос, чтобы увидеть, есть ли у них ключ. Существует было бы предпочтительнее в этом случае.

но, в зависимости от оптимизатора СУБД, это не может быть иначе.

в качестве примера, когда существует лучше

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

Comments

    Ничего не найдено.