MySQL
Errore 1055: ONLY_FULL_GROUP_BY: qualcosa non è nella clausola GROUP BY ...
Ricerca…
introduzione
GROUP BY
, o almeno per rendere più difficile per gli sviluppatori di query di scrittura da masterizzare.
Osservazioni
Per molto tempo, MySQL ha contenuto una nota estensione non standard di GROUP BY
, che consente comportamenti stravaganti in nome dell'efficienza. Questa estensione ha permesso a innumerevoli sviluppatori in tutto il mondo di utilizzare GROUP BY
nel codice di produzione senza capire completamente cosa stessero facendo.
In particolare, è una cattiva idea utilizzare SELECT *
in una query GROUP BY
, perché una clausola GROUP BY
standard richiede l'enumerazione delle colonne. Sfortunatamente, molti sviluppatori lo hanno fatto.
Leggi questo. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
Il team di MySQL ha cercato di risolvere questa disfunzione senza incasinare il codice di produzione. Hanno aggiunto un flag sql_mode
in 5.7.5 denominato ONLY_FULL_GROUP_BY
per forzare il comportamento standard. In una recente versione, hanno attivato quel flag per impostazione predefinita. Quando hai aggiornato MySQL locale alla 5.7.14, la flag è stata attivata e il tuo codice di produzione, dipendente dalla vecchia estensione, ha smesso di funzionare.
Se hai recentemente iniziato a ottenere errori 1055, quali sono le tue scelte?
- correggi le query SQL offende o chiedi ai loro autori di farlo.
- ripristinare la versione di MySQL compatibile immediatamente con il software applicativo utilizzato.
- cambia la
sql_mode
del tuo server per sbarazzarti della nuova modalitàONLY_FULL_GROUP_BY
.
È possibile modificare la modalità eseguendo un comando SET
.
SET sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
dovrebbe fare il trucco se lo fai subito dopo che l'applicazione si connette a MySQL.
Oppure, puoi trovare il file init nella tua installazione MySQL , localizzare la riga sql_mode=
e cambiarlo per omettere ONLY_FULL_GROUP_BY
e riavviare il tuo server.
Utilizzo e abuso di GROUP BY
SELECT item.item_id, item.name, /* not SQL-92 */
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id
mostrerà le righe in una tabella chiamata item
e mostrerà il conteggio delle righe correlate in una tabella denominata uses
. Funziona bene, ma sfortunatamente non è SQL-92 standard.
Perchè no? poiché la clausola SELECT
(e la clausola ORDER BY
) nelle query GROUP BY
deve contenere colonne
- menzionato nella clausola
GROUP BY
, o - funzioni di aggregazione come
COUNT()
,MIN()
e simili.
La clausola SELECT
questo esempio menziona item.name
, una colonna che non soddisfa uno di questi criteri. MySQL 5.6 e precedenti rifiuteranno questa query se la modalità SQL contiene ONLY_FULL_GROUP_BY
.
Questa query di esempio può essere eseguita per conformarsi allo standard SQL-92 modificando la clausola GROUP BY
, in questo modo.
SELECT item.item_id, item.name,
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id, item.name
Lo standard SQL-99 successivo consente a un'istruzione SELECT
di omettere le colonne non aggregate dalla chiave di gruppo se il DBMS può dimostrare una dipendenza funzionale tra di esse e le colonne chiave di gruppo. Poiché item.name
dipende dal punto di item.item_id
funzionale su item.item_id
, l'esempio iniziale è valido SQL-99. MySQL ha ottenuto un test di dipendenza funzionale nella versione 5.7. L'esempio originale funziona con ONLY_FULL_GROUP_BY
.
Utilizzo improprio di GROUP BY per restituire risultati imprevedibili: la legge di Murphy
SELECT item.item_id, uses.category, /* nonstandard */
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id
mostrerà le righe in una tabella chiamata item e mostrerà il conteggio delle righe correlate in una tabella denominata uses. uses.category
anche il valore di una colonna chiamata uses.category
.
Questa query funziona in MySQL (prima che ONLY_FULL_GROUP_BY
flag ONLY_FULL_GROUP_BY
). Usa l'estensione non standard di MySQL a GROUP BY
.
Ma la query ha un problema: se più righe nella tabella uses
corrispondono alla condizione ON
nella clausola JOIN
, MySQL restituisce la colonna della category
da una sola di quelle righe. Quale fila? Lo scrittore della query e l'utente dell'applicazione non lo sanno in anticipo. Formalmente parlando, è imprevedibile : MySQL può restituire qualsiasi valore desiderato.
Imprevedibile è come casuale, con una differenza significativa. Ci si potrebbe aspettare che una scelta casuale cambi di volta in volta. Pertanto, se una scelta fosse casuale, potreste rilevarla durante il debug o il test. Il risultato imprevedibile è peggiore: MySQL restituisce lo stesso risultato ogni volta che si utilizza la query, finché non lo fa. A volte è una nuova versione del server MySQL che causa un risultato diverso. A volte è una tabella in crescita a causare il problema. Cosa può andare storto, andrà male, e quando non te lo aspetti. Si chiama Murphy's Law .
Il team di MySQL ha lavorato per rendere più difficile per gli sviluppatori commettere questo errore. Le versioni più recenti di MySQL nella sequenza 5.7 hanno un flag sql_mode
chiamato ONLY_FULL_GROUP_BY
. Quando viene impostato questo flag, il server MySQL restituisce l'errore 1055 e si rifiuta di eseguire questo tipo di query.
Utilizzo errato di GROUP BY con SELECT * e come risolverlo.
A volte una query appare come questa, con un *
nella clausola SELECT
.
SELECT item.*, /* nonstandard */
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id
Tale query deve essere sottoposta a refactoring per essere conforme allo standard ONLY_FULL_GROUP_BY
.
Per fare ciò, abbiamo bisogno di una subquery che usi correttamente GROUP BY
per restituire il valore number_of_uses
per ogni item_id
. Questa sottoquery è breve e dolce, perché ha solo bisogno di guardare la tabella degli uses
.
SELECT item_id, COUNT(*) number_of_uses
FROM uses
GROUP BY item_id
Quindi, possiamo unirmi a quella sottoquery con la tabella degli item
.
SELECT item.*, usecount.number_of_uses
FROM item
JOIN (
SELECT item_id, COUNT(*) number_of_uses
FROM uses
GROUP BY item_id
) usecount ON item.item_id = usecount.item_id
Ciò consente alla clausola GROUP BY
di essere semplice e corretta e ci consente anche di usare l'identificatore *
.
Nota: tuttavia, gli sviluppatori saggi evitano di utilizzare l'identificatore *
in ogni caso. Di solito è meglio elencare le colonne che desideri in una query.
ANY_VALUE ()
SELECT item.item_id, ANY_VALUE(uses.tag) tag,
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id
mostra le righe in una tabella chiamata item
, il conteggio delle righe correlate e uno dei valori nella tabella correlata chiamata uses
.
Puoi pensare a questa funzione ANY_VALUE()
come a uno strano tipo di funzione aggregata. Invece di restituire un conteggio, somma o massimo, istruisce il server MySQL a scegliere, arbitrariamente, un valore dal gruppo in questione. È un modo per aggirare l'errore 1055.
ANY_VALUE()
attenzione quando usi ANY_VALUE()
nelle query nelle applicazioni di produzione.
Dovrebbe davvero essere chiamato SURPRISE_ME()
. Restituisce il valore di alcune righe nel gruppo GROUP BY. Quale riga restituisce è indeterminata. Ciò significa che dipende interamente dal server MySQL. Formalmente, restituisce un valore imprevedibile.
Il server non sceglie un valore casuale, è peggio di quello. Restituisce lo stesso valore ogni volta che si esegue la query, finché non lo fa. Può cambiare o meno quando una tabella cresce o si riduce, o quando il server ha più o meno RAM, o quando la versione del server cambia, o quando Marte è in retrogrado (qualunque cosa ciò significhi), o per nessuna ragione.
Sei stato avvertito.