Mysql
 sql >> Database >  >> RDS >> Mysql

L'ordine dei campi in una clausola WHERE influisce sulle prestazioni in MySQL?

SQL è stato progettato per essere un linguaggio dichiarativo, non procedurale. Quindi Query Optimizer non considerare l'ordine dei predicati della clausola where nel determinare come applicarli.

Probabilmente semplificherò molto la seguente discussione su un ottimizzatore di query SQL. Ne ho scritto un anno fa, in questo modo (è stato molto divertente!). Se vuoi davvero approfondire la moderna ottimizzazione delle query, consulta SQL Tuning , da O'Reilly.

In un semplice Query Optimizer, l'istruzione SQL viene prima compilata in un albero di algebra relazionale operazioni. Ciascuna di queste operazioni accetta una o più tabelle come input e produce un'altra tabella come output. Scansione è una scansione sequenziale che legge una tabella dal database. Ordina produce una tabella ordinata. Seleziona produce una tabella le cui righe sono selezionate da un'altra tabella in base a una condizione di selezione. Progetto produce una tabella con solo alcune colonne di un'altra tabella. Prodotto incrociato prende due tabelle e produce una tabella di output composta da ogni possibile abbinamento delle loro righe.

In modo confuso, la clausola SQL SELECT è compilata in un'algebra relazionale Progetto , mentre la clausola WHERE si trasforma in un'algebra relazionale Seleziona . La clausola FROM si trasforma in uno o più Join , ciascuno prendendo due tavoli e producendo un tavolo fuori. Esistono altre operazioni di algebra relazionale che coinvolgono l'unione di insiemi, l'intersezione, la differenza e l'appartenenza, ma manteniamo le cose semplici.

Questo albero ha davvero bisogno di essere ottimizzato. Ad esempio, se hai:

select E.name, D.name 
from Employee E, Department D 
where E.id = 123456 and E.dept_id = D.dept_id

con 5.000 dipendenti in 500 reparti, l'esecuzione di un albero non ottimizzato produrrà ciecamente tutte le possibili combinazioni di un dipendente e un reparto (un prodotto incrociato ) e quindi Seleziona fuori solo l'unica combinazione che era necessaria. La Scansione di Employee produrrà una tabella di 5.000 record, la Scansione of Department produrrà una tabella di 500 record, il Cross Product di queste due tabelle produrrà una tabella di 2.500.000 di record e Seleziona su E.id prenderà quella tabella di 2.500.000 di record e scarterà tutto tranne uno, il record che era desiderato.

[I veri processori di query cercheranno di non materializzare tutte queste tabelle intermedie in memoria, ovviamente.]

Quindi Query Optimizer percorre l'albero e applica varie ottimizzazioni. Uno è dividere ogni Seleziona in una catena di Seleziona , uno per ciascuna delle Seleziona originali Le condizioni di massimo livello, quelle e-ed insieme. (Questa è chiamata "forma normale congiuntiva".) Quindi l'individuo più piccolo Seleziona vengono spostati nell'albero e fusi con altre operazioni di algebra relazionale per formarne di più efficienti.

Nell'esempio sopra, l'ottimizzatore spinge prima Seleziona su E.id =123456 sotto il costoso Cross Product operazione. Ciò significa il prodotto incrociato produce solo 500 righe (una per ogni combinazione di quel dipendente e un reparto). Quindi il livello superiore Seleziona for E.dept_id =D.dept_id filtra le 499 righe indesiderate. Non male.

Se è presente un indice nel campo ID dipendente, l'ottimizzatore può combinare la Scansione di Dipendente con Seleziona su E.id =123456 per formare un indice veloce Ricerca . Ciò significa che solo una riga Employee viene letta in memoria dal disco anziché 5.000. Le cose stanno migliorando.

L'ultima grande ottimizzazione è prendere il Seleziona su E.dept_id =D.dept_id e combinalo con il Cross Product . Questo lo trasforma in un'algebra relazionale Equijoin operazione. Questo non fa molto da solo. Ma se c'è un indice su Department.dept_id, allora la Scansione sequenziale di livello inferiore del Dipartimento che alimenta l'Equijoin può essere trasformato in un indice molto veloce Ricerca del record del nostro dipartimento di un dipendente.

Ottimizzazioni minori implicano il push di Progetto operazioni al ribasso. Se il livello superiore della query richiede solo E.name e D.name e le condizioni richiedono E.id, E.dept_id e D.dept_id, allora Scansione le operazioni non devono creare tabelle intermedie con tutte le altre colonne, risparmiando spazio durante l'esecuzione della query. Abbiamo trasformato una query terribilmente lenta in due ricerche nell'indice e non molto altro.

Per approfondire la domanda originale, supponiamo che tu abbia:

select E.name 
from Employee E 
where E.age > 21 and E.state = 'Delaware'

L'albero dell'algebra relazionale non ottimizzato, una volta eseguito, scansiona i 5.000 dipendenti e produce, ad esempio, i 126 in Delaware che hanno più di 21 anni. Query Optimizer ha anche un'idea approssimativa dei valori nel database. Potrebbe sapere che la colonna E.state contiene i 14 stati in cui l'azienda ha sedi e qualcosa sulle distribuzioni E.age. Quindi prima vede se uno dei campi è indicizzato. Se E.state lo è, ha senso utilizzare quell'indice per selezionare solo il piccolo numero di dipendenti che il Query Processor sospetta si trovi nel Delaware in base alle sue ultime statistiche calcolate. Se solo E.age lo è, il Query Processor probabilmente deciderà che non ne vale la pena, dal momento che il 96% di tutti i dipendenti ha 22 anni e più. Quindi, se E.state è indicizzato, il nostro Query Processor interrompe la Seleziona e unisce E.state ='Delaware' con Scansione per trasformarlo in una Scansione dell'indice molto più efficiente .

Diciamo in questo esempio che non ci sono indici su E.state ed E.age. Il combinato Seleziona l'operazione avviene dopo la "Scansione" sequenziale del Dipendente. Fa la differenza quale condizione in Seleziona è fatto prima? Probabilmente non un grande affare. Il Query Processor potrebbe lasciarli nell'ordine originale nell'istruzione SQL, oppure potrebbe essere un po' più sofisticato e guardare alla spesa prevista. Dalle statistiche, troverebbe di nuovo che la condizione E.state ='Delaware' dovrebbe essere più altamente selettiva, quindi invertirebbe le condizioni e lo farebbe prima, in modo che ci siano solo 126 E.age> 21 confronti invece di 5.000 . Oppure potrebbe rendersi conto che i confronti di uguaglianza di stringhe sono molto più costosi di quelli di interi e lasciare l'ordine da solo.

In ogni caso, tutto questo è molto complesso ed è molto improbabile che l'ordine delle condizioni sintattiche faccia la differenza. Non me ne preoccuperei a meno che tu non abbia un vero problema di prestazioni e il tuo fornitore di database utilizzi l'ordine delle condizioni come suggerimento.