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

MySQL:curiosità totale GROUP BY CON ROLLUP

Perché non stai SELEZIONANDO l'elemento per il quale stai RAGGRUPPANDO. Se hai detto:

GROUP BY c.printable_name

Otterresti il ​​NULL previsto. Tuttavia stai raggruppando per una colonna diversa, quindi MySQL non sa che printable_name sta prendendo parte a un gruppo di rollup e seleziona qualsiasi vecchio valore da quella colonna, nel join di all registrazioni. (Quindi è possibile che vedrai altri paesi oltre all'Uzbekistan.)

Questo fa parte di un problema più ampio con MySQL che è permissivo su ciò che puoi SELECT in una query GROUP BY. Ad esempio, puoi dire:

SELECT gender FROM registrations GROUP BY country;

e MySQL sceglierà felicemente uno dei valori di genere per una registrazione da ciascun paese, anche se non esiste un nesso causale diretto (noto anche come "dipendenza funzionale") tra paese e genere. Altri DBMS rifiuteranno il comando di cui sopra sulla base del fatto che non è garantito che ci sia un genere per paese.(*)

Ora, questo:

SELECT c.printable_name AS 'Country', count(*) AS '#' 
FROM registrations r 
INNER JOIN country c ON r.country = c.country_id 
GROUP BY country

va bene, perché c'è una dipendenza funzionale tra r.country e c.printable_name (supponendo che tu abbia descritto correttamente il tuo country_id come CHIAVE PRIMARIA).

Tuttavia, l'estensione WITH ROLLUP di MySQL è un po' un trucco nel modo in cui funziona. Nella fase della riga di rollup alla fine, scorre l'intero set di risultati di pre-raggruppamento per acquisire i suoi valori e quindi imposta la colonna di raggruppamento su NULL. Non annulla anche altre colonne che hanno una dipendenza funzionale da quella colonna. Probabilmente dovrebbe, ma attualmente MySQL non comprende davvero l'intera questione delle dipendenze funzionali.

Quindi se selezioni c.printable_name ti mostrerà il valore del nome del paese che ha scelto casualmente, e se selezioni c.country_id ti mostrerà l'ID del paese che ha scelto casualmente — anche se c.country_id è il criterio di unione, quindi deve essere lo stesso di r.country, che è NULL!

Quello che puoi fare per aggirare il problema è:

  • raggruppa invece per nome_stampabile; dovrebbe essere OK se printable_names sono univoci, oppure
  • seleziona "r.country" e printable_name e verifica che sia NULL oppure
  • dimentica CON ROLLUP ed esegui una query separata per la somma finale. Sarà un po' più lento ma sarà anche conforme ANSI SQL-92 in modo che la tua app possa funzionare su altri database.

(*:MySQL ha un'opzione SQL_MODE ONLY_FULL_GROUP_BY questo dovrebbe risolvere questo problema, ma va troppo oltre e ti consente solo di selezionare colonne da GROUP BY, non colonne che hanno una dipendenza funzionale da GROUP BY. Quindi farà fallire anche le query valide, rendendolo generalmente inutile.)