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

Come GROUP BY correttamente in MySQL?

La prima cosa da chiarire è che SQL non è MySQL.

In SQL standard non è consentito raggruppare in base a un sottoinsieme dei campi non aggregati. La ragione è molto semplice. Supponiamo che io stia eseguendo questa query:

SELECT color, owner_name, COUNT(*) FROM cars
GROUP BY color

Quella domanda non avrebbe alcun senso. Anche cercare di spiegarlo sarebbe impossibile. Di sicuro sta selezionando i colori e contando la quantità di auto per colore. Tuttavia, sta anche aggiungendo il owner_name campo e possono esserci molti proprietari per un dato colore, come nel caso del White colore. Quindi se ci possono essere molti owner_name valori per un singolo color che risulta essere l'unico campo nel GROUP BY clausola... quindi quale owner_name verrà restituito?

Se è necessario restituire un owner_name quindi è necessario aggiungere una sorta di criteri per selezionarne solo uno, ad esempio il primo in ordine alfabetico, che in questo caso sarebbe John . Tali criteri risulteranno nell'aggiunta di una funzione aggregata MIN(owner_name) e quindi la query avrà di nuovo senso poiché raggrupperà almeno per tutti i campi non aggregati nell'istruzione select.

Come puoi vedere, c'è una ragione chiara e pratica per cui l'SQL standard non è flessibile nel raggruppamento. In caso contrario, potresti trovarti di fronte a situazioni imbarazzanti in cui il valore di una colonna sarà imprevedibile, e non è una bella parola, in particolare se la query in corso ti mostra le transazioni del tuo conto bancario.

Detto questo, allora perché MySQL dovrebbe consentire query che potrebbero non avere senso? E ancora peggio, l'errore nella query sopra potrebbe essere rilevato sintatticamente! La risposta breve è:prestazioni. La risposta lunga è che ci sono alcune situazioni in cui, sulla base delle relazioni di dati, ottenere un valore imprevedibile dal gruppo risulterà in un valore prevedibile.

Se non l'hai ancora capito, l'unico modo in cui puoi prevedere il valore che otterrai prendendo un elemento imprevedibile da un gruppo sarà se tutti gli elementi nel gruppo sono gli stessi. Un chiaro esempio di questa situazione è nella query di esempio nella tua stessa domanda. Guarda come owner_id e owner_name si riferisce nella tabella. È chiaro che dato qualsiasi owner_id , per esempio. 2 , puoi avere un solo owner_name distinto . Pur avendo molte righe, scegliendone una, otterrai Mike come risultato. Nel gergo formale del database questo può essere spiegato come owner_id determina funzionalmente owner_name .

Diamo un'occhiata più da vicino a quella query MySQL completamente funzionante:

SELECT owner_id, owner_name, COUNT(*) total FROM cars
GROUP BY owner_id

Dato qualsiasi owner_id questo restituirebbe lo stesso owner_name , quindi aggiungendolo al GROUP BY clausola non risulterà in più righe restituite. Anche aggiungendo una funzione aggregata MAX(owner_name) non risulterà in meno righe restituite. I dati risultanti saranno esattamente gli stessi. In entrambi i casi, la query verrebbe immediatamente trasformata in una query SQL standard legale poiché almeno tutti i campi non aggregati verrebbero raggruppati per. Quindi ci sono 3 approcci per ottenere gli stessi risultati.

Tuttavia, come accennato in precedenza, questo raggruppamento non standard ha un vantaggio in termini di prestazioni. Puoi controllare questo link così sottovalutato in cui questo è spiegato per maggiori dettagli ma citerò la parte più importante:

Una cosa che vale la pena ricordare è che i risultati non sono necessariamente errati ma piuttosto indeterminato . In altre parole, ottenere i risultati attesi non significa che hai scritto la query giusta. Scrivere la query giusta ti darà sempre i risultati attesi.

Come puoi vedere, potrebbe valere la pena applicare questa estensione MySQL a GROUP BY clausola. Ad ogni modo, se questo non è ancora chiaro al 100%, allora esiste una regola pratica che assicurerà che il tuo raggruppamento sia sempre corretto:Raggruppa sempre, almeno, per tutti i campi non aggregati nella clausola select . Potresti sprecare alcuni cicli della CPU in determinate situazioni, ma è meglio che restituire indeterminato risultati. Se sei ancora terrorizzato dall'idea di non raggruppare correttamente, modifica ONLY_FULL_GROUP_BY La modalità SQL potrebbe essere l'ultima risorsa :)

Possa il tuo raggruppamento essere corretto e performante... o almeno corretto.