Mysql
 sql >> Datenbank >  >> RDS >> Mysql

MySQL:Totale GROUP BY WITH ROLLUP Neugier

Weil Sie nicht das Element auswählen, nach dem Sie gruppieren. Wenn Sie sagten:

GROUP BY c.printable_name

Sie würden die erwartete NULL erhalten. Sie gruppieren jedoch nach einer anderen Spalte, sodass MySQL nicht weiß, dass printable_name an einer Rollup-Gruppe teilnimmt, und einen beliebigen alten Wert aus dieser Spalte im Join von all auswählt Anmeldungen. (Es ist also möglich, dass Sie andere Länder als Usbekistan sehen.)

Dies ist Teil eines umfassenderen Problems, bei dem MySQL zulässt, was Sie in einer GROUP BY-Abfrage auswählen können. Sie können beispielsweise sagen:

SELECT gender FROM registrations GROUP BY country;

und MySQL wählt gerne einen der Geschlechtswerte für eine Registrierung aus jedem Land aus, obwohl es keinen direkten kausalen Zusammenhang (auch bekannt als „funktionale Abhängigkeit“) zwischen Land und Geschlecht gibt. Andere DBMSs lehnen den obigen Befehl mit der Begründung ab, dass es nicht garantiert ist, dass es ein Geschlecht pro Land gibt.(*)

Nun, das hier:

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

ist in Ordnung, weil es eine funktionale Abhängigkeit zwischen r.country und c.printable_name gibt (vorausgesetzt, Sie haben Ihre country_id korrekt als PRIMARY KEY beschrieben).

Die WITH ROLLUP-Erweiterung von MySQL ist jedoch ein bisschen wie ein Hack in der Art und Weise, wie sie funktioniert. In der Rollup-Zeilenstufe am Ende wird die gesamte Ergebnismenge der Vorgruppierung durchlaufen, um ihre Werte abzurufen, und dann setzt die Gruppieren-nach-Spalte auf NULL. Er löscht nicht auch andere Spalten, die eine funktionale Abhängigkeit von dieser Spalte haben. Das sollte es wahrscheinlich, aber MySQL versteht die ganze Sache mit funktionalen Abhängigkeiten derzeit nicht wirklich.

Wenn Sie also c.printable_name auswählen, wird Ihnen der zufällig ausgewählte Wert des Ländernamens angezeigt, und wenn Sie c.country_id auswählen, wird Ihnen die zufällig ausgewählte Länder-ID angezeigt — obwohl c.country_id das Join-Kriterium ist, so muss es sein das gleiche wie r.country, das NULL ist!

Was Sie tun können, um das Problem zu umgehen, ist:

  • Gruppieren Sie stattdessen nach printable_name; sollte OK sein, wenn printable_names eindeutig sind, oder
  • wählen Sie „r.country“ sowie printable_name und prüfen Sie, ob es NULL ist, oder
  • vergiss WITH ROLLUP und führe eine separate Abfrage für die Endsumme durch. Dies wird etwas langsamer sein, aber es wird auch ANSI SQL-92-kompatibel sein, sodass Ihre App auf anderen Datenbanken funktionieren könnte.

(*:MySQL hat eine SQL_MODE-Option ONLY_FULL_GROUP_BY das soll dieses Problem beheben, geht aber viel zu weit und lässt Sie nur Spalten aus dem GROUP BY auswählen, nicht Spalten, die eine funktionale Abhängigkeit vom GROUP BY haben. Es wird also auch gültige Abfragen fehlschlagen lassen, was es im Allgemeinen unbrauchbar macht.)