Czy istnieje najlepsza praktyka między użyciem formatu LEFT JOIN lub NOT EXISTS?
Jakie są korzyści z używania jednego nad drugim?
Jeśli nie, które powinny być preferowane?
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)
Korzystam z zapytań w programie Access przeciwko bazie danych SQL Server.
sql-server
join
exists
Michael Richardson
źródło
źródło
WHERE A.idx NOT IN (...)
jest nie identyczne ze względu na zachowanie trójwartościowegoNULL
(czyliNULL
nie jest równyNULL
(ani nierówne), dlatego jeśli którykolwiekNULL
ztableB
was dostanie nieoczekiwane rezultaty!)Odpowiedzi:
Największą różnicą jest to, że łączenie nie istnieje, to (jak napisano)
SELECT *
.W pierwszym przykładzie, można uzyskać wszystkie kolumny z obu
A
iB
, podczas gdy w drugim przykładzie, można uzyskać tylko z kolumnA
.W SQL Server drugi wariant jest nieco szybszy w bardzo prostym wymyślonym przykładzie:
Utwórz dwie przykładowe tabele:
Wstaw 10 000 wierszy do każdej tabeli:
Usuń co 5 rząd z drugiej tabeli:
Wykonaj dwa
SELECT
warianty instrukcji testowych :Plany realizacji:
Drugi wariant nie musi wykonywać operacji filtrowania, ponieważ może korzystać z lewego operatora anty-semi join.
źródło
Logicznie są identyczne, ale
NOT EXISTS
są bliżej AntiSemiJoin, o który prosisz, i są ogólnie preferowane. Podkreśla także lepiej, że nie można uzyskać dostępu do kolumn w B, ponieważ jest on używany tylko jako filtr (w przeciwieństwie do udostępniania ich z wartościami NULL).Wiele lat temu (SQL Server 6.0 ish)
LEFT JOIN
było szybsze, ale od dawna tak nie było. Te dniNOT EXISTS
są nieznacznie szybsze.Największy wpływ w programie Access polega na tym, że
JOIN
metoda musi zakończyć sprzężenie przed filtrowaniem go, tworząc połączony zestaw w pamięci. UżycieNOT EXISTS
go sprawdza wiersz, ale nie przydziela miejsca na kolumny. Ponadto przestaje szukać, gdy znajdzie wiersz. Wydajność różni się nieco bardziej w programie Access, ale ogólna ogólna zasada jest taka, żeNOT EXISTS
jest nieco szybsza. Byłbym mniej skłonny powiedzieć, że to „najlepsza praktyka”, ponieważ wiąże się z tym więcej czynników.źródło
Wyjątkiem, który zauważyłem,
NOT EXISTS
będąc lepszym (choć marginalnym),LEFT JOIN ... WHERE IS NULL
jest używanie serwerów połączonych .Z analizy planów wykonania wynika, że
NOT EXISTS
operator jest wykonywany w sposób zagnieżdżony. Przy czym jest on wykonywany dla poszczególnych wierszy (co, jak sądzę, ma sens).Przykładowy plan wykonania przedstawiający to zachowanie:
źródło
INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=y
a następnie uruchomienieNOT EXISTS (...)
klauzuli przeciwko tej tymczasowej kopii bazy danych.Ogólnie silnik utworzy plan wykonania w oparciu o:
Dla 4):
Plan „nie istnieje” zachęca do opracowania planu opartego na wyszukiwaniu w tabeli B. Jest to dobry wybór, gdy tabela A jest mała, a tabela B jest duża (a indeks istnieje na B).
Plan „antijoin” jest dobrym wyborem, gdy tabela A jest bardzo duża lub tabela B jest bardzo mała lub nie ma indeksu na B i zwraca duży zestaw wyników.
Jest to jednak „zachęta”, jak ważony wkład. Silny (1), (2), (3) często dokonuje wyboru dla (4) dyskusji.
(Ignorując efekt twojego przykładu zwracania różnych kolumn ze względu na *, adresowany przez odpowiedź @MaxVernon.).
źródło