Qual è la differenza tra NOT EXISTS vs NOT IN vs. LEFT JOIN WHERE IS NULL?


151

Mi sembra che puoi fare la stessa cosa in una query SQL usando NOT EXISTS, NOT IN o LEFT JOIN WHERE IS NULL. Per esempio:

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

Non sono sicuro di aver corretto tutta la sintassi, ma queste sono le tecniche generali che ho visto. Perché dovrei scegliere di utilizzare l'uno sull'altro? Le prestazioni differiscono ...? Quale di questi è il più veloce / efficiente? (Se dipende dall'implementazione, quando dovrei usarli?)


6
Molti motori SQL comuni ti danno la possibilità di vedere un piano di esecuzione. In questo modo è spesso possibile individuare differenze significative in termini di efficienza per le query logicamente equivalenti. Il successo di qualsiasi metodo dipende da fattori quali le dimensioni della tabella, quali indici sono presenti e altri.
Chris Farmer,

2
@wich: nessun database si preoccupa di cosa ritorni esattamente all'interno della EXISTSclausola. Puoi tornare *, NULLo qualsiasi altra cosa: tutto questo sarà ottimizzato via.
Quassnoi,

2
@wich - perché? Entrambi qui: techonthenet.com/sql/exists.php e qui: msdn.microsoft.com/en-us/library/ms188336.aspx sembrano usare * ...
froadie

8
@wich: non si tratta di "esprimere interesse". Si tratta del parser di query che richiede di inserire qualcosa tra SELECTe FROM. Ed *è semplicemente più facile da scrivere. Sì, SQLha una certa somiglianza con un linguaggio naturale, ma è analizzato ed eseguito da una macchina, una macchina programmata. Non è che si rompa improvvisamente nel tuo cubicolo e grida "smetti di chiedere i campi extra in una EXISTSquery perché sono stufo di analizzarli e poi di buttarli via!". Va bene con un computer, davvero.
Quassnoi,

1
@Quassnoi se scrivessi codice al solo scopo di una macchina di interpretarlo, il codice sembrerebbe orribile, e sfortunatamente molte persone lavorano così. Se tuttavia scrivi il codice in un'altra ottica, scrivendo il codice per esprimere ciò che vuoi che la macchina faccia come comunicato ai tuoi colleghi, scriverai un codice migliore e più gestibile. Sii intelligente, scrivi codice per le persone, non per il computer.
che il

Risposte:


139

In breve:

NOT INè un po 'diverso: non corrisponde mai se NULLnella lista c'è solo un singolo .

  • In MySQL, NOT EXISTSè un po 'meno efficiente

  • In SQL Server, LEFT JOIN / IS NULLè meno efficiente

  • In PostgreSQL, NOT INè meno efficiente

  • In Oracle, tutti e tre i metodi sono uguali.


1
Grazie per i collegamenti! E grazie per la rapida panoramica ... Il mio ufficio sta bloccando il collegamento per qualche motivo: P, ma lo controllerò non appena arrivo a un normale computer.
froadie,

2
Un altro punto è che se table1 .acontiene NULLla EXISTSquery non restituirà questa riga ma la NOT INquery sarà table2vuota. Colonne Nullable NOT IN vs. NOT EXISTS: SQL Server
Martin Smith,

@MartinSmith: NULL NOT IN ()valuta vero (non NULL), proprio comeNOT EXISTS (NULL = column)
Quassnoi,

2
@Quassnoi - ehm, buon punto, ho capito nel modo sbagliato. Il NOT EXISTSsarà sempre restituire la riga, ma NOT INfarà solo se la query sub non restituisce alcuna riga.
Martin Smith,

5

Se il database è in grado di ottimizzare la query, i primi due verranno trasformati in qualcosa di simile al terzo.

Per situazioni semplici come quelle in questione, ci dovrebbe essere poca o nessuna differenza, poiché verranno eseguite tutte come join. Nelle query più complesse, il database potrebbe non essere in grado di creare un join dalle query not ine not exists. In tal caso le query diventeranno molto più lente. D'altra parte, un join può anche comportarsi male se non c'è un indice che può essere usato, quindi solo perché usi un join non significa che sei al sicuro. Dovresti esaminare il piano di esecuzione della query per sapere se potrebbero esserci problemi di prestazioni.


2

Supponendo che tu stia evitando i null, sono tutti modi per scrivere un anti-join usando SQL standard.

Un'omissione evidente è l'equivalente usando EXCEPT:

SELECT a FROM table1
EXCEPT
SELECT a FROM table2

Nota in Oracle è necessario utilizzare l' MINUSoperatore (probabilmente un nome migliore):

SELECT a FROM table1
MINUS
SELECT a FROM table2

Parlando di sintassi proprietaria, potrebbero esserci anche equivalenti non standard che vale la pena indagare a seconda del prodotto che si sta utilizzando, ad esempio OUTER APPLYin SQL Server (qualcosa del genere):

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

Quando è necessario inserire i dati nella tabella con la chiave primaria multi-campo, considerare che sarà molto più veloce (ho provato in Access ma penso in qualsiasi database) di non verificare che "non esistano record con" tali "valori nella tabella", - piuttosto basta inserire nella tabella e i record in eccesso (con la chiave) non verranno inseriti due volte.


0

La prospettiva delle prestazioni evita sempre di usare parole chiave inverse come NOT IN, NOT EXISTS, ... Perché per controllare gli elementi inversi, DBMS deve scorrere tutti gli elementi disponibili e rilasciare la selezione inversa.


1
E cosa proponi come soluzione alternativa quando ne hai effettivamente bisogno NOT?
pranza il

Bene, quando non c'è alcuna opzione di causa, dobbiamo usare le operazioni NOT ed è per questo che esistono. La migliore pratica è evitarli quando abbiamo altre soluzioni alternative.
Lahiru Cooray,

@onedayquando, se un ottimizzatore trasforma una query e restituisce il risultato sbagliato, allora è un bug
David דודו Markovitz

@DuduMarkovitz: sì e se contatti il ​​team di SQL Server e riconoscono il bug ma si rifiutano di risolverlo perché dicono che ciò potrebbe rallentare l'esecuzione delle query, quindi è un bug che devi affrontare .
giorno

@onedaywhen - Presumo che questo non fosse uno scenario ipotetico :-) Ricordi per caso i dettagli del bug?
David Mark Markitz
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.