2009-03-18 14 views
15

Ich habe zwei Fragen. Einer von ihnen macht Sinn für mich, der andere nicht. Erste:MySQL: Insgesamt GROUP BY MIT ROLLUP Neugier

SELECT gender AS 'Gender', count(*) AS '#' 
    FROM registrations 
    GROUP BY gender WITH ROLLUP 

, dass das gibt mir:

Gender  # 
Female  20 
Male  19 
NULL  39 

ich die Zählung So erhalten, und die Gesamtzahl. Was ich erwartet habe. Nächste:

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

Country   # 
Denmark   9 
Norway   10 
Sweden   18 
United States 1 
Uzbekistan  1 
Uzbekistan  39 

Gleiches Ergebnis. Aber warum bekomme ich Usbekistan für die Summe?

+0

Wie sicher sind Sie, dass das Problem nicht ist, wie die Ergebnisse angezeigt werden? –

+0

es passiert sowohl in meinem Code und in der phpmyadmin Sache. Ich wünschte, ich könnte versuchen, es mit regulären MySQL Abfrage-Browser oder etwas zu testen, aber keinen Zugriff haben. – Svish

+0

Ich spielte auch nur mit Rollup. Ich denke, das Problem hier ist, dass Sie der Länder-ID den Namen des Landes beitreten, so dass alle NULL-Werte diesen Join nicht tun, und aus irgendeinem Grund den letzten Wert des Namens behalten, anstatt keinen Namen zu haben. – sphism

Antwort

36

Aber warum ich Usbekistan für die Gesamt bekommen ??

Da Sie das Element, das Sie gruppieren, nicht auswählen. Wenn Sie sagten:

GROUP BY c.printable_name 

Sie würden die erwartete NULL erhalten. Sie gruppieren jedoch nach einer anderen Spalte, so dass MySQL nicht weiß, dass printable_name an einer Rollup-Gruppe teilnimmt, und wählt einen alten Wert aus dieser Spalte in der Verknüpfung alle Registrierungen aus. (So ​​ist es möglich, dass Sie andere Länder als Usbekistan sehen werden.)

Dies ist Teil eines größeren Problems mit MySQL, das erlaubt, was Sie in einer GROUP BY-Abfrage auswählen können. Zum Beispiel können Sie sagen:

SELECT gender FROM registrations GROUP BY country; 

und MySQL wird gerne von jedem Land eine der Geschlechter Werte für eine Registrierung auswählen, auch wenn es kein direkter Kausalzusammenhang (auch bekannt als „funktionale Abhängigkeit“) zwischen Land und Geschlecht . Andere DBMS werden den obigen Befehl auf dem Gelände verweigern, dass es nicht ein Geschlecht ist garantiert pro Land sein (*)

Nun, dies:.

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

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

Allerdings ist die WITH ROLLUP-Erweiterung von MySQL ein bisschen ein Hack in der Art, wie es funktioniert. In der Rollup-Row-Phase am Ende wird sie über die gesamte Vorgruppierungs-Ergebnismenge ausgeführt, um ihre Werte zu erfassen, und dann setzt die Group-by-Spalte auf NULL. Es werden auch keine anderen Spalten gelöscht, die eine funktionale Abhängigkeit von dieser Spalte haben. Wahrscheinlich sollte es, aber MySQL versteht derzeit nicht das ganze über funktionale Abhängigkeiten.

Wenn Sie also c.printable_name auswählen, wird Ihnen angezeigt, welcher Landnamenswert zufällig ausgewählt wurde, und wenn Sie c auswählen.country_id zeigt Ihnen die Länder-ID an, die zufällig ausgewählt wurde - obwohl c.country_id das Join-Kriterium ist, muss also dasselbe sein wie r.country, was NULL ist!

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

  • Gruppe von printable_name statt; sollte in Ordnung sein, wenn printable_names einzigartig sind, oder
  • „r.country“ sowie printable_name wählen, und dass für seine NULL überprüfen oder
  • WITH ROLLUP und macht eine separate Abfrage für die Endsumme vergessen. Dies wird etwas langsamer sein, aber es wird auch ANSI SQL-92-konform sein, damit Ihre App in anderen Datenbanken funktionieren kann.

(*: MySQL hat eine SQL_MODE Option ONLY_FULL_GROUP_BY, die dieses Problem adressieren soll, aber es geht viel zu weit und nur können Sie Spalten aus der GROUP BY wählen, keine Spalten, die eine funktionelle Abhängigkeit von der GROUP haben BY. so wird es auch machen scheitern gültige Abfragen, es in der Regel nutzlos.)

+0

+1 tolle Informationen – diEcho

0

Coz, wenn Sie die Methode JOIN verwenden, wird das folgende NULL-Element des Arrays den Wert des vorherigen NOT NULL-Elements haben. Aber ich bin mir nicht sicher. Das ist meine Erfahrung, wenn ich es in PHP verwende.

hm ... es gibt ein anderes Problem ... 'Land' kann nicht sein, weil es Name der Tabelle ist. Ändere dich also für etwas anderes. Dann wird das letzte Ergebnis NULL anzeigen. Hier ist mein Vorschlag:

$result = mysql_query("SELECT c.printable_name AS 'countryp', count(*) AS '#' 
FROM registrations r, country c WHERE r.country = c.country_id 
GROUP BY countryp WITH ROLLUP"); 

while($row = @mysql_fetch_array($result)) { 
    $r1 = $row["countryp"]; 
    $r2 = $row["#"]; 
    if ($r1 == NULL) $r1 = 'Total'; 
    echo "$r1 $r2<br />"; 
} 
+0

könnte ich die Abfrage auf die gleiche Weise arbeiten, aber mit NULL als das gesamte Ding wie in der anderen? – Svish

+0

das ist einfach komisch ... ich verstehe das nicht, haha. Wenn ich AS 'countryp' und GROUP BY countryp benutze, funktioniert es. Wenn ich 'name' verwende, tut es das nicht. Wenn ich 'qe' verwende, tut es ... – Svish

0
SELECT ifnull(c.printable_name, "Total Registration = ") AS 'Country', count(*) AS '#' 
FROM registrations r 
INNER JOIN country c ON r.country = c.country_id 
GROUP BY country WITH ROLLUP; 

Dies würde drucken ‚Gesamt Registrierung = 39‘ und würde die letzte Zeile/Datensatz sein.