Molto spesso mi è capitato di dover rispondere sui forum ad alcune domande come:
"
Ma è meglio usare una WHERE o una HAVING?" (a prescindere dal fatto che sono diversi, e non solo per sintassi
, altrimenti perchè renderli disponibili?)
"Usare il filtro nella ON invece che nella WHERE è più performante?"
"Ma perchè non posso fare GROUP BY usando gli alias?"
..
oltre a queste, molto orientate alla sintassi, ci sono poi tante altre domande relative alle performance, legate a subquery, utilizzo di oggetti temporanei, ordinamenti, e quant'altro.
Tante di queste domande, possono essere facilmente risposte
da un pdf che amo con tutto il cuore, e che porto con me sempre anche durante i miei corsi/consulenze/sessioni..
Si tratta di un workflow LOGICO che il query processor rispetta per creare il relativo processo FISICO di raccolta e ritorno dei dati.
Qualcuno che mi segue l'ha anche visto e commentato con me in "aula" e qualcuno invece ha proprio creato il workflow
.
Questo workflow va di pari passo con l'ordine di esecuzione (logico) di una query generica:
1) FROM
2) ON
3) OUTER
4) WHERE
5) GROUP BY
6) CUBE | ROLLUP
7) HAVING
8) SELECT
9) DISTINCT
10) TOP
11) ORDER BY
Guardando il pdf questo viene naturale. E di certo è anche molto più facile rispondere a tutte quelle domande di cui sopra.
Questo post non è il primo ad essere scritto, anzi, in tanti hanno contribuito, su tutti
Pinal Dave, in
questo suo post.
Perchè scrivo? Perchè non mi stancherò mai di dire che bisogna guardare questi documenti, approfondire, capire come lavora il motore e non dare per scontato di conoscerne il comportamento.
Questo workflow riassume il comportamento logico (ma non necessariamente quello fisico) che assume il motore si SQL Server (nella parte denominata Query Processor) nei confronti dei comandi che inviamo.
Ma che succede quando eseguiamo il comando?
- In primis viene fatto il parsing dello statement, e viene controllata la sintassi.
- successivamente viene creato un tree con gli step logici da effettuare (ad alto livello, senza scendere troppo nel dettaglio, molto simile alla query di partenza)
- a questo punto parte la risoluzione nomi e delle dipendenze
- il risultato dell'operazione precedente viene passata al Query optimizer per la selezione del piano di esecuzione (o l'eventuale generazione) in base ai costi di ognuno di quelli disponibili per il comando
- il Query optimizer lega il processo logico alle operazioni fisiche da applicare
- la query viene eseguita seguendo il piano e i dati vengono tornati
Quando scriviamo una query, non è detto che venga fatto il prodotto cartesiano tra due tabelle, soprattutto se gli oggetti che utilizziamo hanno indici definiti. Inoltre, certe fasi che nel processo logico sono collocate "tutte in un punto", ma possono essere fisicamente considerate ed eseguite singolarmente dal motore in diversi istanti. In definitiva, il processo logico è il macro processo che l'optimizer segue, ma che praticamente viene tradotto in un processo fisico differente. Vengono applicati, durante l'esecuzione del processo fisico, algoritmi di matching, di filtro, di selezione, di ordinamento, di utilizzo degli indici che consentono di eseguire la query nella maniera più performante possibile per il motore.
Quindi, perchè il logical query processing? Beh, perchè applicandolo, comunque si ottengono i medesimi risultati che il processo fisico effettivamente ritorna. In altre parole, fisicamente si ottiene lo stesso risultato che si otterrebbe applicando il processo logico.
Seppure la parte fisica si comporti diversamente da quella logica, è comunque da considerare valido l'ordine di esecuzione delle query indicato sopra. Quindi, giusto per rispondere alle domande indicate all'inizio del post:
"Ma è meglio usare una WHERE o una HAVING?"
la HAVING viene eseguita in un secondo momento rispetto ad una where e ragiona su dati eventualmente preaggregati e già filtrati da una ulteriore precedente WHERE. E' proprio un'esigenza diversa.
Quindi, non fare una HAVING se si può fare una WHERE, clausola tramite la quale si determina pesantemente il tempo di risposta della query (pensiamo all'utilizzo degli indici).
"Usare il filtro nella ON invece che nella WHERE è più performante?"
Se si tratta di INNER JOIN in effetti è praticamente la stessa cosa (il Query Processor lo capisce). Se si tratta di outer, il discorso cambia completamente. Siccome la ON viene eseguita prima della WHERE, la ON determina il criterio con cui determinare le righe, appunto, OUTER (quelle NULL che non fanno match per il criterio di join). Quindi in questo caso l'ordine di esecuzione è proprio importante.
"Ma perchè non posso fare GROUP BY usando gli alias?"
Perchè ancora non è stata fatta una SELECT e quindi ancora gli alias non sono disponibili.
Un post come questo meriterebbe ore e ore di discussione. Tuttavia ho voluto sottolineare alcuni punti su cui riflettere.
Stay tuned!