Какой из этих запросов быстрее?
НЕ СУЩЕСТВУЕТ:
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.
[Редактировать]
Только что нашел эту полезную статью: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx
Я думаю, что я буду придерживаться НЕ СУЩЕСТВУЕТ.
sql
sql-server
notin
ilitirit
источник
источник
NOT IN
запрос:SELECT "A".* FROM "A" WHERE "A"."id" NOT IN (SELECT "B"."Aid" FROM "B" WHERE "B"."Uid" = 2)
почти в 30 раз быстрее, чем этотNOT EXISTS
:SELECT "A".* FROM "A" WHERE (NOT (EXISTS (SELECT 1 FROM "B" WHERE "B"."user_id" = 2 AND "B"."Aid" = "A"."id")))
Ответы:
Я всегда по умолчанию
NOT EXISTS
.Планы выполнения могут быть одинаковыми в данный момент , но если один столбец изменен в будущем , чтобы
NULL
ВЛЯЕТСЯNOT IN
версию нужно будет делать больше работы (даже если нетNULL
s не фактически присутствует в данных) и семантикаNOT IN
еслиNULL
s есть настоящий вряд ли будут те, которые вы хотите в любом случае.Когда ни
Products.ProductID
или[Order Details].ProductID
позволитьNULL
ВЛЯЮТСЯNOT IN
будет рассматриваться идентично следующего запроса.Точный план может отличаться, но для моего примера я получаю следующее.
Кажется, довольно распространенным заблуждением является то, что коррелированные подзапросы всегда «плохие» по сравнению с объединениями. Они, конечно, могут быть, когда они вынуждают план вложенных циклов (подзапрос оценивается строка за строкой), но этот план включает логический оператор анти-полусоединения. Анти-полусоединения не ограничиваются вложенными циклами, но могут также использовать хеш-функции или объединения слиянием (как в этом примере).
Если
[Order Details].ProductID
этоNULL
-able запрос , то становитсяПричина этого заключается в том, что правильная семантика, если она
[Order Details]
содержитNULL
ProductId
s, не должна возвращать результатов. См. Дополнительную анти-полусоединение и катушку с подсчетом строк, чтобы убедиться, что это добавлено в план.Если
Products.ProductID
также изменить наNULL
-able, то запрос становитсяПричина этого в том, что a
NULL
Products.ProductId
не должно возвращаться в результатах, кроме случаев, когдаNOT IN
подзапрос вообще не возвращал результатов (т. Е.[Order Details]
Таблица пуста). В каком случае это должно быть. В плане для моих образцов данных это реализовано добавлением еще одного анти-полусоединения, как показано ниже.Эффект этого показан в сообщении в блоге, уже связанном Бакли . В этом примере количество логических операций чтения увеличилось с 400 до 500 000.
Кроме того, тот факт, что один
NULL
может уменьшить количество строк до нуля, очень затрудняет оценку количества элементов. Если SQL Server предполагает, что это произойдет, но на самом деле в данных не былоNULL
строк, остальная часть плана выполнения может быть катастрофически хуже, если это только часть более крупного запроса, с ненадлежащими вложенными циклами, вызывающими повторное выполнение дорогостоящей подпрограммы. дерево например .Однако это не единственно возможный план выполнения для столбца
NOT IN
onNULL
-able. Эта статья показывает еще один для запроса кAdventureWorks2008
базе данных.Для столбца
NOT IN
onNOT NULL
илиNOT EXISTS
против столбца, обнуляемого или не обнуляемого, он дает следующий план.Когда столбец меняется на
NULL
-able,NOT IN
план теперь выглядитЭто добавляет дополнительный внутренний оператор соединения к плану. Этот аппарат объясняется здесь . Это все, что нужно для преобразования предыдущего поиска по одному коррелированному индексу
Sales.SalesOrderDetail.ProductID = <correlated_product_id>
в два поиска по внешней строке. Дополнительный включенWHERE Sales.SalesOrderDetail.ProductID IS NULL
.Так как это находится под анти-полусоединением, если этот возвращает какие-либо строки, второй поиск не произойдет. Однако, если
Sales.SalesOrderDetail
он не содержитNULL
ProductID
s, он удвоит количество требуемых операций поиска.источник
NOT EXISTS
функционирую так, как я ожидаюNOT IN
(а это не так).Также имейте в виду, что NOT IN не эквивалентен NOT EXISTS, когда дело доходит до нуля.
Этот пост объясняет это очень хорошо
http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/
источник
Если планировщик выполнения говорит, что они одинаковы, они одинаковы. Используйте тот, который сделает ваше намерение более очевидным - в данном случае второй.
источник
На самом деле, я считаю, что это будет самым быстрым:
источник
У меня есть таблица, которая содержит около 120 000 записей, и мне нужно выбрать только те, которые не существуют (сопоставленные с столбцом varchar) в четырех других таблицах с числом строк приблизительно 1500, 4000, 40000, 200. Все участвующие таблицы имеют уникальный индекс на соответствующей
Varchar
колонке.NOT IN
заняло около 10 минут,NOT EXISTS
заняло 4 секунды.У меня есть рекурсивный запрос, который мог иметь какой-то ненастроенный раздел, который мог бы внести вклад в 10 минут, но другой вариант, занимающий 4 секунды, объясняет, по крайней мере мне, что
NOT EXISTS
это намного лучше или, по крайней мере, этоIN
иEXISTS
не совсем то же самое и всегда стоит проверьте, прежде чем идти вперед с кодом.источник
В вашем конкретном примере они одинаковы, потому что оптимизатор выяснил, что вы пытаетесь сделать, то же самое в обоих примерах. Но возможно, что в нетривиальных примерах оптимизатор может этого не делать, и в этом случае есть причины отдавать предпочтение одному другому.
NOT IN
должно быть предпочтительным, если вы тестируете несколько строк в вашем внешнем выборе. Подзапрос внутриNOT IN
оператора может быть оценен в начале выполнения, и временная таблица может быть проверена по каждому значению во внешнем выборе, вместо того, чтобы повторно выполнять подвыбор каждый раз, как того требуетNOT EXISTS
оператор.Если подзапрос должен быть соотнесен с внешним выбором, то это
NOT EXISTS
может быть предпочтительным, поскольку оптимизатор может обнаружить упрощение, которое предотвращает создание каких-либо временных таблиц для выполнения той же функции.источник
Я использовал
и обнаружил, что это дает неправильные результаты (Под неправильным я подразумеваю никаких результатов). Как был NULL в TABLE2.Col1.
При изменении запроса на
дал мне правильные результаты.
С тех пор я начал использовать NOT EXISTS везде, где.
источник
Они очень похожи, но не совсем одинаковы.
С точки зрения эффективности, я обнаружил, что левое соединение является пустым выражением более эффективным (когда нужно выбрать множество строк, то есть)
источник
Если оптимизатор говорит, что они одинаковы, учитывайте человеческий фактор. Я предпочитаю видеть не существует :)
источник
Модель таблицы базы данных
Предположим, у нас есть две таблицы в нашей базе данных, которые образуют отношение таблицы «один ко многим».
student
Таблица является родителем, иstudent_grade
является дочерней таблицей , так как он имеет student_id столбец внешнего ключа , ссылающийся Ид столбец первичного ключа в таблице студента.student table
Содержит следующие две записи:И в
student_grade
таблице хранятся оценки, полученные студентами:SQL СУЩЕСТВУЕТ
Допустим, мы хотим, чтобы все ученики получили 10 баллов по математике.
Если нас интересует только идентификатор студента, мы можем выполнить запрос, подобный следующему:
Но приложение заинтересовано в отображении полного имени, а
student
не только идентификатора, поэтому нам также нужна информация изstudent
таблицы.Чтобы отфильтровать
student
записи, которые имеют 10 баллов по математике, мы можем использовать оператор EXISTS SQL, например:При выполнении запроса выше, мы видим, что выбрана только строка Алиса:
Внешний запрос выбирает
student
столбцы строк, которые мы хотим вернуть клиенту. Однако в предложении WHERE используется оператор EXISTS со связанным внутренним подзапросом.Оператор EXISTS возвращает true, если подзапрос возвращает хотя бы одну запись, и false, если строка не выбрана. Механизм базы данных не должен полностью выполнять подзапрос. Если сопоставляется одна запись, оператор EXISTS возвращает true и выбирается связанная строка запроса.
Внутренний подзапрос коррелируется, потому что столбец student_id
student_grade
таблицы сопоставляется со столбцом id внешней таблицы студентов.SQL НЕ СУЩЕСТВУЕТ
Давайте рассмотрим, что мы хотим выбрать всех учеников, у которых оценка не ниже 9. Для этого мы можем использовать NOT EXISTS, что сводит на нет логику оператора EXISTS.
Следовательно, оператор NOT EXISTS возвращает true, если базовый подзапрос не возвращает запись. Однако, если внутреннему подзапросу соответствует одна запись, оператор NOT EXISTS вернет false, и выполнение подзапроса может быть остановлено.
Чтобы сопоставить все записи учеников, у которых нет связанных student_grade со значением ниже 9, мы можем выполнить следующий запрос SQL:
При выполнении запроса выше мы видим, что сопоставляется только запись Алисы:
Таким образом, преимущество использования операторов SQL EXISTS и NOT EXISTS состоит в том, что выполнение внутреннего подзапроса может быть остановлено, если найдена соответствующая запись.
источник
Это зависит..
не будет относительно медленным, не слишком ограничивает размер проверки запроса, чтобы увидеть, если они вводят ключ. EXISTS будет предпочтительнее в этом случае.
Но, в зависимости от оптимизатора СУБД, это может быть не иначе.
Как пример того, когда EXISTS лучше
источник
IN
иEXISTS
получить тот же план в SQL Server . Вопрос в любом случае оNOT IN
противNOT EXISTS
.