Zapytanie MySQL „NOT IN”

181

Chciałem uruchomić proste zapytanie, aby wyrzucić wszystkie wiersze, w Table1których wartość kolumny głównej nie występuje w kolumnie w innej tabeli ( Table2).

Próbowałem użyć:

SELECT * FROM Table1 WHERE Table1.principal NOT IN Table2.principal

Zamiast tego generuje błąd składniowy. Wyszukiwarka Google zaprowadziła mnie na fora, na których ludzie mówili, że MySQL nie obsługuje NOT INi należy zastosować coś niezwykle złożonego. Czy to prawda? Czy popełniam straszny błąd?

Kshitij Saxena -KJ-
źródło
1
A jeśli chcę podobne dane z trzech tabel. Mam na myśli, że jedna tabela1 zawiera 2000 wpisów, pozostałe dwie tabele 2 i 3 zawierają po 500 wpisów, wszystkie mają wspólną nazwę „nazwa”. Jak możemy uzyskać wszystkie szczegóły z tabeli 1, których nie ma w tabelach 2 i 3 na podstawie „nazwy”. Czy możemy użyć NOT IN dwa razy, jeśli tak, to w jaki sposób ...?

Odpowiedzi:

310

Aby użyć IN, musisz mieć zestaw, zamiast tego użyj tej składni:

SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)
Julien Lebosquain
źródło
85
Ostrożnie, kiedy table2.principalmożna NULL. W takim przypadku NOT INzawsze będzie zwracany, FALSEponieważ NOT INjest traktowany jako <> ALL, który porównuje wszystkie wiersze z podzapytania jak Table1.principal <> table2.principal, co kończy się niepowodzeniem w porównaniu z NULL: Table1.principal <> NULLnie spowoduje TRUE. Aby rozwiązać: NOT IN (SELECT principal FROM table2 WHERE principal IS NOT NULL).
Basti
4
Dzięki za komentarz @ Basti! Spędziłem dużo czasu próbując zrozumieć, dlaczego zapytanie nie działa zgodnie z oczekiwaniami.
gvas
3
Nie zapomnij unikać używania „SELECT *” na liście „NOT IN”. Musisz wybrać konkretną kolumnę. W przeciwnym razie otrzymasz ten błąd: stackoverflow.com/questions/14046838/…
Lorien Brune
165

Odpowiedź na pytanie dotyczące podpowiedzi została już udzielona, ​​ale należy pamiętać, że w wielu przypadkach LEFT JOINmożna to zrobić szybciej:

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

Jeśli chcesz sprawdzić wiele tabel, aby upewnić się, że nie ma ich w żadnej z tabel (jak w komentarzu SRKR), możesz użyć tego:

SELECT table1.*
FROM table1
LEFT JOIN table2 ON table2.name=table1.name
LEFT JOIN table3 ON table3.name=table1.name
WHERE table2.name IS NULL AND table3.name IS NULL
Lukáš Lalinský
źródło
2
W moich własnych testach miałem taką samą wydajność dla obu NOT IN& LEFT JOIN. +1 zarówno
BufferStack
po uruchomieniu zapytania należy uzyskać takie same wyniki bez względu na wewnętrzne buforowanie bazy danych
Toote
Dla mnie wydajność była znacznie lepsza. Przeglądałem różne tabele, a oni mieli ustawione klucze obce.
Keenora Fluffball
36

NOT IN vs. NOT EXISTS vs. LEFT JOIN / IS NULL w MySQL

MySQL, podobnie jak wszystkie inne systemy oprócz SQL Server, jest w stanie zoptymalizować LEFT JOIN/IS NULL zwrócić, FALSEgdy tylko zostanie znaleziona pasująca wartość, i jest to jedyny system, który zechciał udokumentować to zachowanie. […] Ponieważ MySQL nie jest w stanie używać algorytmów HASHi MERGEłączyć się, jedyne, ANTI JOINco potrafi, toNESTED LOOPS ANTI JOIN

[…]

Zasadniczo [ NOT IN] jest dokładnie tym samym planem, z którego LEFT JOIN/ IS NULLkorzysta, mimo że plany te są wykonywane przez różne gałęzie kodu i wyglądają inaczej w wynikach EXPLAIN. Algorytmy są w rzeczywistości takie same, a zapytania kończą się w tym samym czasie.

[…]

Trudno jest podać dokładny powód [spadku wydajności podczas używania NOT EXISTS] , ponieważ spadek ten jest liniowy i nie wydaje się zależeć od rozkładu danych, liczby wartości w obu tabelach itp., O ile oba pola są indeksowane. Ponieważ w MySQL są trzy fragmenty kodu, które w zasadzie wykonują jedno zadanie, możliwe jest, że kod odpowiedzialny za wykonanie EXISTSpewnego rodzaju dodatkowej kontroli zajmuje więcej czasu.

[…]

MySQL może zoptymalizować wszystkie trzy metody, aby zrobić coś w rodzaju NESTED LOOPS ANTI JOIN. […] Jednak te trzy metody generują trzy różne plany, które są wykonywane przez trzy różne fragmenty kodu. Kod, który wykonuje EXISTSpredykat, jest o około 30% mniej wydajny […]

Dlatego najlepszym sposobem wyszukiwania brakujących wartości w MySQL jest użycie LEFT JOIN/ IS NULLlub NOT INzamiast NOT EXISTS.

(podkreślenia dodane)

engin
źródło
7

Niestety wydaje się, że jest to problem ze stosowaniem przez MySql klauzuli „NOT IN”, zrzut ekranu poniżej pokazuje opcję zapytania podrzędnego zwracającą błędne wyniki:

mysql> show variables like '%version%';
+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| innodb_version          | 1.1.8                        |
| protocol_version        | 10                           |
| slave_type_conversions  |                              |
| version                 | 5.5.21                       |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | Linux                        |
+-------------------------+------------------------------+
7 rows in set (0.07 sec)

mysql> select count(*) from TABLE_A where TABLE_A.Pkey not in (select distinct TABLE_B.Fkey from TABLE_B );
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.07 sec)

mysql> select count(*) from TABLE_A left join TABLE_B on TABLE_A.Pkey = TABLE_B.Fkey where TABLE_B.Pkey is null;
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> select count(*) from TABLE_A where NOT EXISTS (select * FROM TABLE_B WHERE TABLE_B.Fkey = TABLE_A.Pkey );
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> 
Legna
źródło