В чем разница между NOT EXISTS и NOT IN против LEFT JOIN, ГДЕ НУЛЬ?

151

Мне кажется, что вы можете сделать то же самое в запросе SQL, используя NOT EXISTS, NOT IN или LEFT JOIN WHERE NULL. Например:

SELECT a FROM table1 WHERE a NOT IN (SELECT a FROM table2)

SELECT a FROM table1 WHERE NOT EXISTS (SELECT * FROM table2 WHERE table1.a = table2.a)

SELECT a FROM table1 LEFT JOIN table2 ON table1.a = table2.a WHERE table1.a IS NULL

Я не уверен, что я правильно понял весь синтаксис, но это общие приемы, которые я видел. Почему я решил бы использовать один поверх другого? Отличается ли производительность ...? Какой из них самый быстрый / самый эффективный? (Если это зависит от реализации, когда я буду использовать каждый из них?)

froadie
источник
6
Многие распространенные механизмы SQL дают вам возможность увидеть план выполнения. Таким образом, вы можете обнаружить значительные различия в эффективности для логически эквивалентных запросов. Успех любого метода зависит от таких факторов, как размер таблицы, какие индексы присутствуют и другие.
Крис Фармер
2
@wich: ни одна база данных не заботится о том, что именно вы возвращаете в EXISTSпредложении. Вы можете вернуться *, NULLили как угодно: все это будет оптимизировано.
Quassnoi
2
@wich - почему? Оба здесь: techonthenet.com/sql/exists.php и здесь: msdn.microsoft.com/en-us/library/ms188336.aspx, кажется, используют * ...
froadie
8
@wich: речь идет не о «выражении интереса». Речь идет о парсере запросов, который требует от вас поместить что-то между SELECTи FROM. И *просто легче набрать. Да, SQLимеет некоторое сходство с естественным языком, но он анализируется и выполняется машиной, запрограммированной машиной. Дело не в том, что он когда-нибудь внезапно взорвется в вашей кабинке и закричит: «Прекратите требовать дополнительные поля в EXISTSзапросе, потому что мне надоело их анализировать, а затем отбрасывать!». Все нормально с компьютером, правда.
Quassnoi
1
@Quassnoi, если бы вы написали код с единственной целью интерпретировать его машиной, код выглядел бы ужасно, и, к сожалению, так мало людей работают. Однако, если вы пишете код в другой оптике, пишете код, который выражает то, что вы хотите, чтобы машина делала в качестве коммюнике своим коллегам, вы будете писать лучший и более понятный код. Будь умным, пиши код для людей, а не для компьютера.
которым

Ответы:

139

В двух словах:

NOT INнемного отличается: он никогда не совпадает, если NULLв списке есть только один .

  • В MySQL, NOT EXISTSнемного менее эффективным

  • В SQL Server, LEFT JOIN / IS NULLменее эффективен

  • В PostgreSQL, NOT INменее эффективен

  • В Oracle, все три метода одинаковы.

Quassnoi
источник
1
Спасибо за ссылки! И спасибо за быстрый обзор ... Мой офис почему-то блокирует ссылку: P, но я проверю это, как только доберусь до обычного компьютера.
froadie
2
Другое дело, что если table1 .aсодержит запрос не возвратит эту строку , но и сделает запрос , если пусто. НЕ ВНУТРИ против НЕ СУЩЕСТВУЕТ Обнуляемые столбцы: SQL ServerNULLEXISTSNOT INtable2
Мартин Смит
@MartinSmith: NULL NOT IN ()оценивается как истинное (не NULL), так же какNOT EXISTS (NULL = column)
Quassnoi
2
@Quassnoi - эээ, хорошая мысль, неправильно понял. NOT EXISTSВсегда будет возвращать строку , но NOT INбудет делать это только если суб - запрос не возвращает ни одной строки.
Мартин Смит
5

Если база данных хороша для оптимизации запроса, два первых будут преобразованы во что-то близкое к третьему.

Для простых ситуаций, подобных тем, о которых вы спрашиваете, различий не должно быть или почти не должно быть, поскольку все они будут выполняться как объединения. В более сложных запросах, база данных может быть не в состоянии сделать присоединиться из ряда not inи not existsqueryes. В этом случае запросы станут намного медленнее. С другой стороны, объединение может также работать плохо, если нет индекса, который можно использовать, так что если вы используете объединение, это еще не значит, что вы в безопасности. Вам нужно будет изучить план выполнения запроса, чтобы определить, есть ли проблемы с производительностью.

Guffa
источник
2

Предполагая, что вы избегаете пустых значений, все они являются способами написания анти-объединения с использованием стандартного SQL.

Очевидным упущением является эквивалент использования EXCEPT:

SELECT a FROM table1
EXCEPT
SELECT a FROM table2

Обратите внимание, что в Oracle вам нужно использовать MINUSоператор (возможно, лучшее имя):

SELECT a FROM table1
MINUS
SELECT a FROM table2

Говоря о проприетарном синтаксисе, могут быть и нестандартные эквиваленты, которые стоит изучить в зависимости от продукта, который вы используете, например, OUTER APPLYв SQL Server (что-то вроде):

SELECT t1.a
  FROM table1 t1
       OUTER APPLY 
       (
        SELECT t2.a
          FROM table2 t2
         WHERE t2.a = t1.a
       ) AS dt1
 WHERE dt1.a IS NULL;
onedaywhen
источник
0

Когда нужно вставить данные в таблицу с многопольным первичным ключом, учтите, что будет гораздо быстрее (я пытался в Access, но я думаю, в любой базе данных) не проверять, что «не существует записей с« такими »значениями в таблице», - скорее просто вставьте в таблицу, и лишние записи (по ключу) не будут вставлены дважды.

baleks
источник
0

С точки зрения производительности всегда избегайте использования обратных ключевых слов, таких как NOT IN, NOT EXISTS, ... Потому что для проверки обратных элементов СУБД необходимо просмотреть все доступные и отбросить обратный выбор.

Лахиру Курей
источник
1
И что вы предлагаете в качестве обходного пути, когда вам действительно нужно NOT?
dnoeth
Хорошо, когда нет никакой причины, нам нужно использовать НЕ операции, и поэтому они существуют. Лучшая практика - избегать их, когда у нас есть другие альтернативные решения.
Лахиру Курей
@onedaywhen, если оптимизатор преобразует запрос и возвращает неправильный результат, это ошибка
Дэвид דודו Марковиц
@DuduMarkovitz: да, и если вы связываетесь с командой SQL Server, и они признают ошибку, но отказываются ее исправлять, потому что говорят, что это может привести к замедлению выполнения запросов, то это ошибка, с которой вам нужно иметь дело .
понедельник,
@onedaywhen - Я предполагаю, что это был не гипотетический сценарий :-) Вы случайно не помните детали ошибки?
Дэвид דודו Марковиц