Ricerca…


introduzione

Di recente, le nuove versioni dei server MySQL hanno iniziato a generare errori 1055 per le query che prima funzionavano. Questo argomento spiega quegli errori. Il team di MySQL ha lavorato per ritirare l'estensione non standard a 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?

  1. correggi le query SQL offende o chiedi ai loro autori di farlo.
  2. ripristinare la versione di MySQL compatibile immediatamente con il software applicativo utilizzato.
  3. 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

  1. menzionato nella clausola GROUP BY , o
  2. 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.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow