Jaka jest różnica między NOT EXISTS a NOT IN a LEFT JOIN WHERE IS NULL?


151

Wydaje mi się, że możesz zrobić to samo w zapytaniu SQL, używając opcji NOT EXISTS, NOT IN lub LEFT JOIN WHERE IS NULL. Na przykład:

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

Nie jestem pewien, czy cała składnia jest poprawna, ale są to ogólne techniki, które widziałem. Dlaczego miałbym wybrać jeden nad drugim? Czy wydajność się różni ...? Który z nich jest najszybszy / najbardziej wydajny? (Jeśli to zależy od implementacji, kiedy użyłbym każdego z nich?)


6
Wiele popularnych silników SQL umożliwia wyświetlenie planu wykonania. W ten sposób często można dostrzec znaczące różnice w wydajności logicznie równoważnych zapytań. Powodzenie każdej metody zależy od czynników, takich jak rozmiar tabeli, obecne indeksy i inne.
Chris Farmer

2
@wich: żadna baza danych nie dba o to, co dokładnie zwracasz w EXISTSklauzuli. Możesz wrócić *, NULLczy cokolwiek innego: to wszystko być zoptymalizowane z dala.
Quassnoi

2
@wich - dlaczego? Oba tutaj: techonthenet.com/sql/exists.php i tutaj: msdn.microsoft.com/en-us/library/ms188336.aspx wydają się używać * ...
froadie

8
@wich: tu nie chodzi o „wyrażanie zainteresowania”. Chodzi o to, że parser zapytań wymaga umieszczenia czegoś między SELECTa FROM. I *jest po prostu łatwiejszy do wpisania. Tak, SQLma pewne podobieństwo do języka naturalnego, ale jest analizowany i wykonywany przez maszynę, zaprogramowaną maszynę. Nie chodzi o to, że kiedykolwiek włamie się nagle do twojego boksu i krzyknie „przestań żądać dodatkowych pól w EXISTSzapytaniu, bo mam dość ich analizowania, a potem wyrzucania!”. Z komputerem jest OK, naprawdę.
Quassnoi,

1
@Quassnoi, jeśli napisałeś kod wyłącznie dla maszyny, która go interpretuje, kod wyglądałby okropnie i niestety sporo osób tak pracuje. Jeśli jednak napiszesz kod w innej optyce, pisząc kod wyrażający to, co chcesz, aby maszyna zrobiła w komunikacie do swoich rówieśników, napiszesz lepszy i łatwiejszy w utrzymaniu kod. Bądź sprytny, pisz kod dla ludzi, a nie dla komputera.
co

Odpowiedzi:


139

W skrócie:

NOT INjest trochę inna: nigdy nie pasuje, jeśli jest tylko jeden NULLna liście.

  • W MySQL, NOT EXISTSjest trochę mniej wydajny

  • W SQL Server, LEFT JOIN / IS NULLjest mniej wydajny

  • W PostgreSQL, NOT INjest mniej wydajny

  • W programie Oraclewszystkie trzy metody są takie same.


1
Dzięki za linki! I dzięki za szybki przegląd ... Moje biuro blokuje łącze z jakiegoś powodu: P ale sprawdzę to, gdy tylko dotrę do zwykłego komputera.
froadie

2
Inną kwestią jest to, że jeśli table1 .azawiera zapytanie nie zwróci ten wiersz ale wyśle zapytanie jeśli jest pusty. NOT IN vs. NOT EXISTS Nullable Columns: SQL ServerNULLEXISTSNOT INtable2
Martin Smith

@MartinSmith: zwraca NULL NOT IN ()wartość true (not NULL), tak jakNOT EXISTS (NULL = column)
Quassnoi

2
@Quassnoi - eee, dobra uwaga, zrozumiałem to w niewłaściwy sposób. NOT EXISTSZawsze zwróci wiersz ale NOT INzrobi tylko więc jeśli zapytanie sub zwraca żadnych wierszy.
Martin Smith

5

Jeśli baza danych dobrze optymalizuje zapytanie, dwie pierwsze zostaną przekształcone w coś zbliżonego do trzeciej.

W przypadku prostych sytuacji, takich jak te, o których mowa, różnica powinna być niewielka lub żadna, ponieważ wszystkie zostaną wykonane jako łączenia. W przypadku bardziej złożonych zapytań baza danych może nie być w stanie wykonać sprzężenia z zapytań not ini not exists. W takim przypadku zapytania będą znacznie wolniejsze. Z drugiej strony, złączenie może również źle działać, jeśli nie ma indeksu, którego można by użyć, więc samo użycie złączenia nie oznacza, że ​​jesteś bezpieczny. Będziesz musiał zbadać plan wykonania zapytania, aby stwierdzić, czy mogą wystąpić problemy z wydajnością.


2

Zakładając, że unikasz wartości null, wszystkie one są sposobami zapisywania anty-złączeń przy użyciu standardowego języka SQL.

Oczywistym pominięciem jest użycie EXCEPT:

SELECT a FROM table1
EXCEPT
SELECT a FROM table2

Uwaga w Oracle musisz użyć MINUSoperatora (prawdopodobnie lepszej nazwy):

SELECT a FROM table1
MINUS
SELECT a FROM table2

Mówiąc o zastrzeżonej składni, mogą istnieć również niestandardowe odpowiedniki, które warto zbadać w zależności od produktu, z którego korzystasz, np. OUTER APPLYW SQL Server (coś takiego):

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;

0

Kiedy trzeba wstawić dane do tabeli z kluczem podstawowym z wieloma polami, weź pod uwagę, że znacznie szybciej (próbowałem w programie Access, ale myślę, że w dowolnej bazie danych) nie będzie sprawdzać, czy „nie istnieje rekordy z„ takimi ”wartościami w tabeli”, - raczej po prostu wstaw do tabeli, a nadmiar rekordów (wg klucza) nie zostanie wstawiony dwukrotnie.


0

Perspektywa wydajności zawsze unikaj używania odwrotnych słów kluczowych, takich jak NIE W, NIE ISTNIEJE, ... Ponieważ aby sprawdzić odwrotne elementy, DBMS musi przejść przez wszystkie dostępne i porzucić odwrotny wybór.


1
A co proponujesz jako obejście, kiedy naprawdę potrzebujesz NOT?
dnoeth

Cóż, gdy nie ma opcji powodu, musimy użyć operacji NOT i dlatego one istnieją. Najlepszą praktyką jest unikanie ich, gdy mamy inne alternatywne rozwiązania.
Lahiru Cooray

@oneday, gdy optymalizator przekształca zapytanie i zwraca nieprawidłowy wynik , oznacza to błąd
David דודו Markovitz

@DuduMarkovitz: tak, a jeśli skontaktujesz się z zespołem SQL Server, a oni potwierdzą błąd, ale odmówią naprawy, ponieważ twierdzą, że może to spowolnić zapytania, to jest to błąd, z którym musisz sobie poradzić .
onedaywhen

@onedaywhen - To nie był hipotetyczny scenariusz, jak sądzę :-) Czy przypadkiem pamiętasz szczegóły błędu?
David דודו Markovitz
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.