Есть ли лучшая практика между использованием LEFT JOIN или NOT EXISTS формата?
Какая польза от использования одного над другим?
Если нет, что должно быть предпочтительным?
SELECT *
FROM tableA A
LEFT JOIN tableB B
ON A.idx = B.idx
WHERE B.idx IS NULL
SELECT *
FROM tableA A
WHERE NOT EXISTS
(SELECT idx FROM tableB B WHERE B.idx = A.idx)
Я использую запросы в Access к базе данных SQL Server.
sql-server
join
exists
Майкл Ричардсон
источник
источник
WHERE A.idx NOT IN (...)
является не тождествен вследствие трехвалентного поведенияNULL
(т.е.NULL
не равенNULL
( и не неравен), поэтому если у вас есть какой - нибудьNULL
вtableB
вас получат неожиданные результаты!)Ответы:
Самая большая разница не в объединении против не существует, то (как написано), то
SELECT *
.В первом примере вы получаете все столбцы из обоих
A
иB
, тогда как во втором примере вы получаете только столбцы изA
.В SQL Server второй вариант немного быстрее в очень простом надуманном примере:
Создайте две таблицы примеров:
Вставьте 10 000 строк в каждую таблицу:
Удалить каждый 5-й ряд из второй таблицы:
Выполните два
SELECT
варианта теста :Планы выполнения:
Во втором варианте не требуется выполнять операцию фильтрации, поскольку он может использовать левый оператор анти-полусоединения.
источник
По логике они идентичны, но
NOT EXISTS
ближе к AntiSemiJoin, который вы запрашиваете, и, как правило, предпочтительнее. Это также подчеркивает, что вы не можете получить доступ к столбцам в B, потому что он используется только как фильтр (в отличие от того, чтобы они были доступны со значениями NULL).Много лет назад (SQL Server 6.0 ish) это
LEFT JOIN
было быстрее, но это не имело место в течение очень долгого времени. В наши дниNOT EXISTS
это немного быстрее.Самое большое влияние в Access состоит в том, что
JOIN
метод должен завершить соединение перед его фильтрацией, создавая объединенный набор в памяти. Его использованиеNOT EXISTS
проверяет строку, но не выделяет место для столбцов. Плюс, он перестает смотреть, как только находит строку. Производительность в Access немного различается, но общее правило,NOT EXISTS
как правило, немного быстрее. Я был бы менее склонен сказать, что это «лучшая практика», так как здесь задействовано больше факторов.источник
Исключение, которое я заметил для
NOT EXISTS
превосходства (хотя и незначительного),LEFT JOIN ... WHERE IS NULL
это при использовании связанных серверов .Из рассмотрения планов выполнения выясняется, что
NOT EXISTS
оператор выполняется в режиме вложенного цикла. При этом он выполняется для каждой строки (что, я думаю, имеет смысл).Пример плана выполнения, демонстрирующий это поведение:
источник
INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=y
затем запустивNOT EXISTS (...)
предложение для этой временной копии базы данных.В общем, движок создаст план выполнения, основанный главным образом на:
Для (4):
План «не существует» поощряет план на основе поиска для таблицы B. Это хороший выбор, когда таблица A мала, а таблица B велика (и индекс B существует).
План «противодействия» является хорошим выбором, когда таблица A очень велика или таблица B очень мала, или индекс B отсутствует, и возвращается большой набор результатов.
Однако это просто «поощрение», как взвешенный вклад. Сильный (1), (2), (3) часто делает выбор для (4) спорным.
(Не обращая внимания на эффект вашего примера, возвращающего разные столбцы из-за *, адресованного ответом @MaxVernon.).
источник