Sottoquery vs join


158

Ho refactored una sezione lenta di un'applicazione che abbiamo ereditato da un'altra società per utilizzare un join interno anziché una subquery come:

WHERE id IN (SELECT id FROM ...)

La query refactored viene eseguita circa 100 volte più veloce. (Da ~ 50 secondi a ~ 0,3) Mi aspettavo un miglioramento, ma qualcuno può spiegare perché fosse così drastico? Le colonne utilizzate nella clausola where sono state tutte indicizzate. SQL esegue la query nella clausola where una volta per riga o qualcosa del genere?

Aggiornamento - Spiega i risultati:

La differenza sta nella seconda parte della query "dove id in ()" -

2   DEPENDENT SUBQUERY  submission_tags ref st_tag_id   st_tag_id   4   const   2966    Using where

vs 1 riga indicizzata con il join:

    SIMPLE  s   eq_ref  PRIMARY PRIMARY 4   newsladder_production.st.submission_id  1   Using index

4
Possibile duplicato della
sottoquery

2
Non un duplicato. Questa domanda riguarda specificamente la notevole differenza di prestazioni. L'altra domanda è più generale, a tempo indeterminato sui pro e contro di ogni approccio e sul perché un approccio sembra più popolare.
Basil Bourque,

@simhumileco Questo non è un miglioramento, non fa differenza, è contrario a ciò che l'autore ha scritto e quel tipo di modifica per lo stile del codice è inappropriato. Quando devo apportare modifiche al codice?
Philipxy,

Ciao @philipxy, non intendevo interferire nel pensiero dell'autore, ma solo per rendere il frammento di codice più leggibile e scritto con più attenzione.
simhumileco,

Risposte:


160

Una "sottoquery correlata" (ovvero, in cui la condizione where dipende dai valori ottenuti dalle righe della query contenuta) verrà eseguita una volta per ogni riga. Una subquery non correlata (una in cui la condizione where è indipendente dalla query contenente) verrà eseguita una volta all'inizio. Il motore SQL fa questa distinzione automaticamente.

Ma sì, spiega il piano ti fornirà i dettagli sporchi.


3
Si noti che DEPENDENT SUBQUERYsignifica esattamente la stessa cosa di "subquery correlata".
Timo,

38

Stai eseguendo la subquery una volta per ogni riga mentre l'unione avviene sugli indici.


5
Non penso sia vero. Il motore SQL dovrebbe eseguire la subquery una sola volta e utilizzare il risultato come elenco.
Dacracot,

8
Ciò dipende: se la sottoquery è in qualche modo correlata con la query esterna (usa i suoi dati), viene eseguita con ogni riga.
qbeuek,

4
Probabilmente è vero in questo caso, ma non è vero in generale.
Amy B,

1
OP EXPLAINdice DEPENDENT SUBQUERY, che è l'indicatore più chiaro di questo comportamento.
Timo,


7

Esegui il piano di spiegazione su ogni versione, ti dirà perché.


6

prima che le query vengano eseguite sul set di dati in cui vengono inserite tramite un Query Optimizer, l'ottimizzatore tenta di organizzare la query in modo tale da rimuovere il maggior numero possibile di tuple (righe) dal set di risultati. Spesso quando si utilizzano subquery (soprattutto quelle non valide) le tuple non possono essere eliminate dal set di risultati fino a quando la query esterna non inizia a essere eseguita.

Senza vedere la query è difficile dire cosa ci fosse di così brutto nell'originale, ma immagino che sarebbe stato qualcosa che l'ottimizzatore non avrebbe potuto fare molto meglio. L'esecuzione di 'spiegazione' ti mostrerà il metodo di ottimizzazione per il recupero dei dati.


4

Guarda il piano di query per ogni query.

Dove in e Join possono in genere essere implementati utilizzando lo stesso piano di esecuzione, quindi in genere non vi è alcuna accelerazione nel passaggio da uno all'altro.


3
Haha, io <3 Sql scrub che votano in basso perché non sanno come leggere i piani di query.
Amy B,

4

L'ottimizzatore non ha fatto un ottimo lavoro. Di solito possono essere trasformati senza alcuna differenza e l'ottimizzatore può farlo.


4

Di solito è il risultato dell'ottimizzatore che non è in grado di capire che la subquery può essere eseguita come join nel qual caso esegue la subquery per ciascun record nella tabella anziché unire la tabella nella subquery rispetto alla tabella che si sta interrogando. Alcuni dei database più "enterprise" sono migliori in questo, ma a volte mancano ancora.


4

Questa domanda è in qualche modo generale, quindi ecco una risposta generale:

Fondamentalmente, le query impiegano più tempo quando MySQL ha tonnellate di righe da ordinare.

Fai questo:

Esegui un EXPLAIN su ciascuna delle query (quella JOIN, quindi quella Subqueried) e pubblica qui i risultati.

Penso che vedere la differenza nell'interpretazione di MySQL di queste domande sarebbe un'esperienza di apprendimento per tutti.


4

La sottoquery dove deve eseguire 1 query per ogni riga restituita. Il join interno deve solo eseguire 1 query.


3

La sottoquery probabilmente stava eseguendo una "scansione completa della tabella". In altre parole, non usare l'indice e restituire troppe righe che il filtro Where della query principale doveva filtrare.

Solo un'ipotesi senza dettagli, ma questa è la situazione comune.


2

Con una subquery, è necessario rieseguire il 2nd SELECT per ciascun risultato e ogni esecuzione restituisce in genere 1 riga.

Con un join, il 2nd SELECT restituisce molte più righe, ma è necessario eseguirlo solo una volta. Il vantaggio è che ora puoi unirti ai risultati e unire le relazioni è ciò in cui un database dovrebbe essere bravo. Ad esempio, forse l'ottimizzatore può individuare come sfruttare al meglio un indice ora.


2

Non è tanto la sottoquery quanto la clausola IN, sebbene i join siano alla base almeno del motore SQL di Oracle e funzionino in modo estremamente rapido.


1
dove in realtà non è intrinsecamente male.
Shawn,

2

Tratto dal manuale di riferimento ( 14.2.10.11 Riscrittura delle sottoquery come join ):

Un JOIN LEFT [OUTER] può essere più veloce di una subquery equivalente perché il server potrebbe essere in grado di ottimizzarlo meglio, un fatto che non è specifico solo per MySQL Server.

Quindi le sottoquery possono essere più lente di LEFT [OUTER] JOINS.

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.