SQL Server IN e EXISTS Performance


115

Sono curioso di sapere quale delle seguenti opzioni sarebbe più efficiente?

Sono sempre stato un po 'cauto nell'utilizzo INperché credo che SQL Server trasformi il set di risultati in una grande IFdichiarazione. Per un set di risultati di grandi dimensioni, ciò potrebbe comportare prestazioni scadenti. Per piccoli set di risultati, non sono sicuro che sia preferibile. Per set di risultati di grandi dimensioni, non EXISTSsarebbe più efficiente?

WHERE EXISTS (SELECT * FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

vs.

WHERE bx.BoxID IN (SELECT BoxID FROM Base WHERE [Rank = 2])

8
Il modo migliore per scoprirlo è provarlo e fare alcune misurazioni.
Klaus Byskov Pedersen

10
c'è got a essere un duplicati gazillion per questo ......
marc_s

5
@marc_s - Probabilmente sì, ma nel tempo che mi ci sarebbe voluto per esaminare tutti i post su questo argomento e trovarne uno adatto al mio caso, ho avuto quattro risposte alla mia domanda.
Randy Minder

7
FYI se hai intenzione il più modo performante, è possibile select 1 from Base...nella vostra where existsdal momento che in realtà non si preoccupano dei risultati, solo che una riga esiste realmente.
brad

2
@marc_s è davvero triste, perché mi sono preso il tempo di esaminare i post per non aggiungere altro cestino a stackoverflow. Non ho bisogno di una risposta su misura per portare a termine il mio lavoro. Questo è il tipo di pensiero che ha aggiunto un duplicato di Gazillion al posto di pochi con buone risposte
IvoC

Risposte:


140

EXISTS sarà più veloce perché una volta che il motore ha trovato un colpo, smetterà di guardare come la condizione si è dimostrata vera.

Con IN, raccoglierà tutti i risultati dalla sottoquery prima dell'ulteriore elaborazione.


4
È un buon punto. L'istruzione IN richiede che SQL Server generi un set di risultati completo e quindi crei una grande istruzione IF, credo.
Randy Minder

72
Questo era vero ma nelle versioni attuali (almeno nel 2008) l'ottimizzatore è molto più intelligente ... in realtà tratta IN () proprio come EXISTS ().
Aaron Bertrand

11
@Aaron - sì, in genere l'ottimizzatore produrrà internamente un piano migliore. Tuttavia, affidarsi a scorciatoie interne potrebbe essere dannoso in scenari più complessi.
Scott Coates,

2
Questo è semplicemente sbagliato. Era il 2010 e lo è ancora.
Magnus

2
IN ed EXISTS hanno lo stesso identico piano di query e IO. Non c'è motivo di pensare che siano diversi nelle prestazioni. controlla le tue statistiche sul tempo e mettiti alla
prova

40

La risposta accettata è miope e la domanda un po 'sciolta in quanto:

1) Non menzionare esplicitamente se un indice di copertura è presente a sinistra, a destra o in entrambi i lati.

2) Nessuno dei due tiene conto della dimensione del lato sinistro di input impostato e del lato destro di input impostato.
(La domanda menziona solo un insieme di risultati di grandi dimensioni ).

Credo che l'ottimizzatore sia abbastanza intelligente da convertire tra "in" vs "esiste" quando c'è una differenza di costo significativa dovuta a (1) e (2), altrimenti può essere usato solo come suggerimento (es. Esiste per incoraggiare l'uso di un indice ricercabile sul lato destro).

Entrambi i moduli possono essere convertiti internamente in moduli di join, invertire l'ordine di join ed essere eseguiti come loop, hash o merge, in base ai conteggi di righe stimati (sinistra e destra) e all'esistenza dell'indice a sinistra, a destra o su entrambi i lati.


3
non so perché questa eccellente risposta non abbia ricevuto più attenzione. La comprensione dell'indice / struttura per entrambe le parti potrebbe avere un impatto sono d'accordo. Ben detto.
SheldonH

L'ottimizzatore fornisce sempre lo stesso piano per INe EXISTS. Prova a trovare un caso in cui non ottengano lo stesso piano (anche se questo non si applica a NOT INe NOT EXISTS)
Martin Smith

@MartinSmith Presumo che tu sappia di cosa stai parlando, ma hai qualche prova che i piani sono sempre gli stessi? Se è così, chiarirebbe il disaccordo decennale qui.
MarredCheese

@MarredCheese - l'onere è sulle persone che affermano che è diverso produrre un singolo esempio di questo
Martin Smith

37

Ho eseguito alcuni test su SQL Server 2005 e 2008 e sia su EXISTS che su IN sono tornati con lo stesso identico piano di esecuzione effettivo, come hanno affermato altri. L'ottimizzatore è ottimale. :)

Qualcosa di cui essere consapevoli, tuttavia, EXISTS, IN e JOIN a volte possono restituire risultati diversi se non si formula correttamente la query: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210 aspx


5

Ci sono molte risposte fuorvianti qui, inclusa quella altamente votata (anche se non credo che le loro operazioni significassero danni). La risposta breve è: questi sono gli stessi.

Ci sono molte parole chiave nel linguaggio (T-) SQL, ma alla fine l'unica cosa che accade veramente sull'hardware sono le operazioni come si vede nel piano di esecuzione delle query.

L'operazione relazionale (teoria matematica) che facciamo quando invochiamo [NOT] INed [NOT] EXISTSè il semi join (anti-join quando si usa NOT). Non è un caso che le corrispondenti operazioni sql-server abbiano lo stesso nome . Non c'è operazione che menzioni INo da EXISTSnessuna parte - solo (anti-) semi join. Pertanto, non è possibile che una scelta INvs logicamente equivalente EXISTSpossa influire sulle prestazioni perché esiste un solo e unico modo, l'operazione di esecuzione (anti) semi join, per ottenere i risultati .

Un esempio:

Domanda 1 ( piano )

select * from dt where dt.customer in (select c.code from customer c where c.active=0)

Domanda 2 ( piano )

select * from dt where exists (select 1 from customer c where c.code=dt.customer and c.active=0)

L'hai provato? In tal caso, puoi condividere il tuo SQL e i tuoi risultati?
UnhandledExcepSean

L'ho testato più volte. Posso creare un altro test case, e lo farò, ma un test case non significa che l'ottimizzatore eseguirà lo stesso identico piano su tabelle con statistiche diverse. Questo potrebbe portare qualcuno a pensare che la risposta sia parziale, ma l'inesistenza di più operatori semijoin è un dato di fatto. Forse troverò un elenco da qualche parte e lo collegherò.
George Menoutis

5

Andrei con EXISTS su IN, vedi sotto il link:

SQL Server: JOIN vs IN vs EXISTS: la differenza logica

Esiste un malinteso comune che IN si comporti allo stesso modo di EXISTS o JOIN in termini di risultati restituiti. Questo semplicemente non è vero.

IN: restituisce true se un valore specificato corrisponde a qualsiasi valore in una sottoquery o in un elenco.

Exists: restituisce true se una sottoquery contiene righe.

Partecipa: unisce 2 gruppi di risultati nella colonna Partecipazione .

Credito del blog: https://stackoverflow.com/users/31345/mladen-prajdic


Wow, grazie per il tuo blog e la spiegazione.
Christian Müller

3

I piani di esecuzione saranno in genere identici in questi casi, ma finché non vedrai come l'ottimizzatore influisce in tutti gli altri aspetti degli indici ecc., Non lo saprai mai.


3

Quindi, IN non è lo stesso di EXISTS né produrrà lo stesso piano di esecuzione.

Di solito EXISTS viene utilizzato in una sottoquery correlata, il che significa che UNIRETE la query interna EXISTS alla query esterna. Ciò aggiungerà più passaggi per produrre un risultato poiché è necessario risolvere i join di query esterni e i join di query interni quindi abbinano le loro clausole where per unirli entrambi.

Di solito IN viene utilizzato senza correlare la query interna con la query esterna e ciò può essere risolto in un solo passaggio (nel migliore dei casi).

Considera questo:

  1. Se si utilizza IN e il risultato della query interna è milioni di righe di valori distinti, probabilmente verrà eseguito PIÙ LENTO di EXISTS dato che la query EXISTS è performante (ha gli indici giusti per unirsi alla query esterna).

  2. Se usi EXISTS e l'unione con la tua query esterna è complessa (richiede più tempo per essere eseguita, nessun indice adatto) rallenterà la query del numero di righe nella tabella esterna, a volte il tempo stimato per il completamento può essere in giorni. Se il numero di righe è accettabile per l'hardware specificato o la cardinalità dei dati è corretta (ad esempio, un numero inferiore di valori DISTINCT in un insieme di dati di grandi dimensioni) IN può eseguire più velocemente di EXISTS.

  3. Tutto quanto sopra sarà notato quando si dispone di una discreta quantità di righe su ogni tabella (per equo intendo qualcosa che supera l'elaborazione della CPU e / o le soglie di ram per la cache).

Quindi la RISPOSTA è DIPENDENTE. Puoi scrivere una query complessa all'interno di IN o EXISTS, ma come regola generale, dovresti provare a usare IN con un insieme limitato di valori distinti ed EXISTS quando hai molte righe con molti valori distinti.

Il trucco sta nel limitare il numero di righe da scansionare.

Saluti,

MarianoC


1

Per ottimizzare il EXISTS, sii molto letterale; qualcosa deve essere semplicemente lì, ma in realtà non è necessario alcun dato restituito dalla sottoquery correlata. Stai solo valutando una condizione booleana.

Così:

WHERE EXISTS (SELECT TOP 1 1 FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

Poiché la sottoquery correlata è RBAR, il primo risultato ottenuto rende la condizione vera e non viene elaborata ulteriormente.


Sarei sempre estremamente cauto nell'usare la codifica LEFT JOIN + NULL, perché è molto facile ottenere risultati persi o distorti se non si è molto attenti nella gestione di NULL. Molto raramente ho trovato una situazione in cui EXISTS o un CTE (per trovare la duplicazione o l'inserimento sintetico per i dati mancanti), non soddisfano gli stessi requisiti e superano il LEFT JOIN + NULL
Josh Lewis

3
TOP 1 dovrebbe essere completamente estraneo (o ridondante per eventi) se usato con EXISTS. EXISTS ritorna sempre non appena trova una riga corrispondente.
Karl Kieninger

Finora non ho riscontrato alcun vantaggio in termini di prestazioni con questo approccio. Si prega di mostrare alcuni screenshot dei piani di esecuzione
DaFi4

-1

Al di sopra della mia testa e non è garantito che sia corretto: credo che il secondo sarà più veloce in questo caso.

  1. Nel primo, la sottoquery correlata probabilmente causerà l'esecuzione della sottoquery per ogni riga.
  2. Nel secondo esempio, la sottoquery dovrebbe essere eseguita solo una volta, poiché non è correlata.
  3. Nel secondo esempio, INandrà in corto circuito non appena trova una corrispondenza.
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.