Query MySQL "NON IN"


181

Volevo eseguire una semplice query per generare tutte le righe in Table1cui un valore di colonna principale non è presente in una colonna in un'altra tabella ( Table2).

Ho provato a usare:

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

Questo invece genera un errore di sintassi. La ricerca di Google mi ha portato a forum in cui la gente diceva che MySQL non supporta NOT INe deve essere usato qualcosa di estremamente complesso. È vero? O sto facendo un errore orrendo?


1
E se volessi dati simili da tre tabelle. Voglio dire, una tabella1 ha 2000 voci, le altre due tabelle 2 e 3 hanno ciascuna 500 voci, tutte hanno un campo comune "nome". Come possiamo ottenere tutti i dettagli dalla tabella 1 che non sono presenti nelle tabelle 2 e 3 in base a "nome". Possiamo usare NOT IN due volte, se sì come ...?

Risposte:


310

Per utilizzare IN, è necessario disporre di un set, utilizzare invece questa sintassi:

SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)

85
Attento quando table2.principalpuò essere NULL. In quel caso NOT INtornerà sempre FALSEperché NOT INviene trattato come <> ALL, che confronta tutte le righe della sottoquery come Table1.principal <> table2.principal, che fallisce quando si confronta con NULL: Table1.principal <> NULLnon si tradurrà in TRUE. Per risolvere il problema: NOT IN (SELECT principal FROM table2 WHERE principal IS NOT NULL).
Basti,

4
Grazie per il commento @Basti! Trascorso molto tempo cercando di capire perché la query non funzionava come previsto.
gvas

3
Non dimenticare di evitare di utilizzare "SELEZIONA *" nell'elenco "NON IN". Devi scegliere una colonna particolare. Altrimenti otterrai questo errore: stackoverflow.com/questions/14046838/…
Lorien Brune

165

L'opzione di subquery ha già ricevuto una risposta, ma in molti casi a LEFT JOINpuò essere un modo più veloce per farlo:

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

Se vuoi controllare più tabelle per assicurarti che non sia presente in nessuna delle tabelle (come nel commento di SRKR), puoi usare questo:

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

2
Nei miei test, ho avuto le stesse prestazioni per entrambi NOT IN& LEFT JOIN. +1 entrambi
BufferStack

una volta che la query è stata eseguita una volta dovresti ottenere gli stessi risultati, non importa cosa sia dovuto alla memorizzazione nella cache interna del database
Toote

Per me la performance è stata decisamente migliore. Ho attraversato diversi tavoli, mentre avevano le chiavi esterne impostate.
Keenora Fluffball

36

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

MySQL, così come tutti gli altri sistemi ad eccezione di SQL Server, è in grado di ottimizzare LEFT JOIN/IS NULL restituire FALSEnon appena viene trovato il valore corrispondente ed è l'unico sistema che ha avuto cura di documentare questo comportamento. […] Dato che MySQL non è in grado di utilizzare HASHe MERGEunire algoritmi, l'unico ANTI JOINche è in grado di essere èNESTED LOOPS ANTI JOIN

[...]

In sostanza, [ NOT IN] è esattamente lo stesso piano che LEFT JOIN/ IS NULLutilizza, nonostante il fatto che questi piani siano eseguiti dai diversi rami del codice e abbiano un aspetto diverso nei risultati di EXPLAIN. Gli algoritmi sono infatti gli stessi in effetti e le query vengono completate contemporaneamente.

[...]

È difficile dire il motivo esatto di [calo delle prestazioni durante l'utilizzo NOT EXISTS] , poiché questo calo è lineare e non sembra dipendere dalla distribuzione dei dati, dal numero di valori in entrambe le tabelle ecc., Purché entrambi i campi siano indicizzati. Dato che in MySQL sono presenti tre pezzi di codice che svolgono essenzialmente un lavoro, è possibile che il codice responsabile EXISTSesegua una sorta di controllo extra che richiede più tempo.

[...]

MySQL può ottimizzare tutti e tre i metodi per fare una sorta di NESTED LOOPS ANTI JOIN. […] Tuttavia, questi tre metodi generano tre piani diversi che vengono eseguiti da tre diversi pezzi di codice. Il codice che esegue EXISTSpredicato è circa il 30% in meno efficiente [...]

Ecco perché il modo migliore per cercare valori mancanti in MySQL è usare un LEFT JOIN/ IS NULLo NOT INpiuttosto che NOT EXISTS.

(sottolineature aggiunte)


7

Sfortunatamente sembra essere un problema con l'utilizzo di MySql della clausola "NOT IN", la seguente schermata mostra l'opzione di query secondaria che restituisce risultati errati:

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> 

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.