Ich habe einen Tisch, der aussieht wie dieser Anrufer 'Makerar'
cname | wmname | avg
--------+-------------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
Und ich möchte den maximalen Durchschnitt für jeden c-Namen auswählen.
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
aber ich werde einen Fehler bekommen,
ERROR: column "makerar.wmname" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
Also mache ich das
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname, wmname;
Dies führt jedoch nicht zu den beabsichtigten Ergebnissen, und die folgende falsche Ausgabe wird angezeigt.
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
Tatsächliche Ergebnisse sollten sein
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | usopp | 5.0000000000000000
Wie kann ich dieses Problem beheben?
Hinweis: Diese Tabelle ist eine ANSICHT, die aus einer vorherigen Operation erstellt wurde.
sql
group-by
aggregate-functions
postgresql-9.1
Zufälliger Typ
quelle
quelle
wmname="usopp"
erwartet und nicht zum Beispielwmname="luffy"
?Antworten:
Ja, dies ist ein häufiges Aggregationsproblem. Vor SQL3 (1999) müssen die ausgewählten Felder in der
GROUP BY
Klausel [*] erscheinen.Um dieses Problem zu umgehen, müssen Sie das Aggregat in einer Unterabfrage berechnen und dann mit sich selbst verknüpfen, um die zusätzlichen Spalten zu erhalten, die Sie anzeigen müssen:
Sie können aber auch Fensterfunktionen verwenden, die einfacher aussehen:
Das einzige, was mit dieser Methode gemacht wird, ist, dass alle Datensätze angezeigt werden (Fensterfunktionen gruppieren sich nicht). Aber es wird das Richtige angezeigt (dh auf
cname
Stufe maximal )MAX
in jeder Zeile für das Land , also liegt es an Ihnen:Die wohl weniger elegante Lösung, um die einzigen
(cname, wmname)
Tupel anzuzeigen, die dem Maximalwert entsprechen, lautet:[*]: Interessanterweise scheinen große Engines es nicht wirklich zu mögen, obwohl die Spezifikation die Auswahl nicht gruppierter Felder erlaubt. Oracle und SQLServer lassen dies überhaupt nicht zu. Früher erlaubte MySQL dies standardmäßig, aber seit 5.7 muss der Administrator diese Option (
ONLY_FULL_GROUP_BY
) manuell in der Serverkonfiguration aktivieren, damit diese Funktion unterstützt wird ...quelle
MAX
anzumelden (siehe Antwort von @ypercube, es gibt auch eine andere Lösung in meiner Antwort), aber nicht so, wie Sie es tun. Überprüfen Sie die erwartete Ausgabe.avg
percname
), schränkt jedoch die Zeilen des Ergebnisses nicht ein (wie vom OP gewünscht). Siehe die tatsächlichen Ergebnisse sollte Absatz in der Frage sein.ONLY_FULL_GROUP_BY
in MySQL 5.7 nicht die Art , wie die SQL - Standard legt fest , zu aktivieren , wenn Spalten aus der weggelassen werden kanngroup by
(oder macht MySQL verhalten sich wie Postgres). Es wird nur auf das alte Verhalten zurückgegriffen, bei dem MySQL stattdessen zufällige (= "unbestimmte") Ergebnisse zurückgibt.In Postgres können Sie auch die spezielle
DISTINCT ON (expression)
Syntax verwenden:quelle
BY cname
?Das Problem bei der Angabe nicht gruppierter und nicht aggregierter Felder in
group by
Auswahlen besteht darin, dass die Engine nicht wissen kann, welches Datensatzfeld in diesem Fall zurückgegeben werden soll. Ist es zuerst? Ist es das letzte? Es gibt normalerweise keine Aufzeichnung, die natürlich dem aggregierten Ergebnis entspricht (min
undmax
Ausnahmen sind).Es gibt jedoch eine Problemumgehung: Machen Sie das erforderliche Feld ebenfalls aggregiert. In posgres sollte dies funktionieren:
Beachten Sie, dass dadurch ein Array aller nach Durchschnitt geordneten Namen erstellt wird und das erste Element zurückgegeben wird (Arrays in Postgres basieren auf 1).
quelle
Mit
rank()
Fensterfunktion :Hinweis
In beiden Fällen werden mehrere Maximalwerte pro Gruppe beibehalten. Wenn Sie nur einen Datensatz pro Gruppe möchten, auch wenn es mehr als einen Datensatz mit einem Durchschnitt von max gibt, sollten Sie die Antwort von @ ypercube überprüfen.
quelle
Für mich geht es nicht um ein "allgemeines Aggregationsproblem", sondern nur um eine falsche SQL-Abfrage. Die einzige richtige Antwort für "Wählen Sie den maximalen Durchschnitt für jeden C-Namen ..." lautet
Das Ergebnis wird sein:
Dieses Ergebnis beantwortet im Allgemeinen die Frage "Was ist das beste Ergebnis für jede Gruppe?" . Wir sehen, dass das beste Ergebnis für Spanien 5 und für Kanada das beste Ergebnis 2 ist. Es ist wahr und es gibt keinen Fehler. Wenn wir auch wmname anzeigen müssen , müssen wir die Frage beantworten: "Was ist die REGEL , um wmname aus der resultierenden Menge auszuwählen?" Lassen Sie uns die Eingabedaten ein wenig ändern, um den Fehler zu klären:
Welches Ergebnis erwarten Sie beim Ausführen dieser Abfrage :
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
? Sollte es seinspain+luffy
oderspain+usopp
? Warum? In der Abfrage wird nicht festgelegt, wie "besserer" WM-Name ausgewählt werden soll wenn mehrere geeignet sind. Daher wird das Ergebnis auch nicht ermittelt. Aus diesem Grund gibt der SQL-Interpreter einen Fehler zurück - die Abfrage ist nicht korrekt.Mit anderen Worten, es gibt keine richtige Antwort auf die Frage "Wer ist der Beste in der
spain
Gruppe?" . Ruffy ist nicht besser als Lysop, weil Lysop die gleiche "Punktzahl" hat.quelle
SELECT cname, id, MAX(avg) FROM makerar GROUP BY cname;
, die diesen irreführenden Fehler verursachte.Dies scheint auch zu funktionieren
quelle
Ich bin kürzlich auf dieses Problem gestoßen, als ich versucht habe, mit zu zählen
case when
, und habe festgestellt, dass das Ändern der Reihenfolge der Anweisungenwhich
undcount
das Problem behebt:Anstatt zu verwenden - in letzterem, wo ich Fehler bekam, dass Äpfel und Orangen in aggregierten Funktionen erscheinen sollten
quelle
which
Aussage?