Wie erhalten Sie die Zeilen, die den Maximalwert für jede gruppierte Menge enthalten?
Ich habe einige übermäßig komplizierte Variationen dieser Frage gesehen und keine mit einer guten Antwort. Ich habe versucht, ein möglichst einfaches Beispiel zusammenzustellen:
Wie würden Sie bei einer solchen Tabelle mit Spalten für Personen, Gruppen und Alter die älteste Person in jeder Gruppe erhalten? (Ein Gleichstand innerhalb einer Gruppe sollte das erste alphabetische Ergebnis liefern.)
Person | Group | Age
---
Bob | 1 | 32
Jill | 1 | 34
Shawn| 1 | 42
Jake | 2 | 29
Paul | 2 | 36
Laura| 2 | 39
Gewünschte Ergebnismenge:
Shawn | 1 | 42
Laura | 2 | 39
mysql
sql
greatest-n-per-group
Yarin
quelle
quelle
Antworten:
Es gibt eine supereinfache Möglichkeit, dies in MySQL zu tun:
Dies funktioniert, weil Sie in MySQL keine nicht gruppierten Spalten aggregieren dürfen. In diesem Fall gibt MySQL nur die erste Zeile zurück. Die Lösung besteht darin, zuerst die Daten so zu ordnen, dass für jede Gruppe zuerst die gewünschte Zeile und dann nach den Spalten gruppiert wird, für die Sie den Wert möchten.
Sie vermeiden komplizierte Unterabfragen, die versuchen, das
max()
usw. zu finden , und auch die Probleme, mehrere Zeilen zurückzugeben, wenn es mehr als eine mit demselben Maximalwert gibt (wie es die anderen Antworten tun würden).Hinweis: Dies ist eine reine MySQL- Lösung. Alle anderen mir bekannten Datenbanken geben einen SQL-Syntaxfehler mit der Meldung "Nicht aggregierte Spalten werden nicht in der Gruppe nach Klausel aufgeführt" oder ähnlichem aus. Da diese Lösung undokumentiertes Verhalten verwendet, möchten die vorsichtigen möglicherweise einen Test einschließen, um zu bestätigen, dass sie weiterhin funktionieren, falls eine zukünftige Version von MySQL dieses Verhalten ändert.
Update Version 5.7:
Seit Version 5.7 enthält die
sql-mode
EinstellungONLY_FULL_GROUP_BY
standardmäßig. Damit dies funktioniert, müssen Sie diese Option nicht haben (bearbeiten Sie die Optionsdatei für den Server, um diese Einstellung zu entfernen).quelle
SELECT
Klausel erscheint und nicht mit einer Aggregatfunktion berechnet wird.SELECT
Klausel nicht funktional von denGROUP BY
Spalten abhängig sind. Wenn es so konfiguriert ist, dass es es akzeptiert (`ONLY_FULL_GROUP_BY` ist deaktiviert), funktioniert es wie in den vorherigen Versionen (dh die Werte dieser Spalten sind unbestimmt).GROUP BY
verdichtet sich das zu einem Datensatz, aber alle Felder werden willkürlich aus den Datensätzen ausgewählt. Es kann sein, dass MySQL derzeit einfach immer die erste Zeile auswählt, aber es könnte genauso gut jede andere Zeile oder sogar Werte aus verschiedenen Zeilen in einer zukünftigen Version auswählen .Die richtige Lösung ist:
Wie es funktioniert:
Es vergleicht jede Zeile
o
mit allen Zeilenb
, die denselben Wert in der SpalteGroup
und einen größeren Wert in der Spalte habenAge
. Jede Zeile, dieo
nicht den Maximalwert ihrer Gruppe in der Spalte hat,Age
stimmt mit einer oder mehreren Zeilen von übereinb
.Das
LEFT JOIN
macht es die älteste Person in der Gruppe entspricht (die Personen umfasst , die allein in ihrer Gruppe sind) mit einer Reihe vollerNULL
s ausb
( "no größten Alter in der Gruppe).Durch
INNER JOIN
die Verwendung stimmen diese Zeilen nicht überein und werden ignoriert.Die
WHERE
Klausel behält nur die Zeilen mitNULL
s in den Feldern bei, aus denen extrahiert wurdeb
. Sie sind die ältesten Personen aus jeder Gruppe.Weitere Lesungen
Diese und viele andere Lösungen werden im Buch SQL Antipatterns: Vermeiden der Fallstricke der Datenbankprogrammierung erläutert
quelle
o.Age = b.Age
beispielsweise Paul aus Gruppe 2 wie Laura auf 39 ist. Wenn wir jedoch kein solches Verhalten wollen, können wir Folgendes tun:ON o.Group = b.Group AND (o.Age < b.Age or (o.Age = b.Age and o.id < b.id))
Sie können sich einer Unterabfrage anschließen, die das
MAX(Group)
und ziehtAge
. Diese Methode ist auf die meisten RDBMS portierbar.quelle
Group = 2, Age = 20
gäbe, würde die Unterabfrage eine davon zurückgeben, aber die Join-ON
Klausel würde mit beiden übereinstimmen , sodass Sie 2 Zeilen mit derselben Gruppe / demselben Alter zurückerhalten würden, obwohl für die anderen Spalten unterschiedliche Werte gelten. eher als einer.Meine einfache Lösung für SQLite (und wahrscheinlich MySQL):
Es funktioniert jedoch nicht in PostgreSQL und möglicherweise auf einigen anderen Plattformen.
In PostgreSQL können Sie die DISTINCT ON- Klausel verwenden:
quelle
Ranking-Methode verwenden.
quelle
:=
- was ist das?Ich bin mir nicht sicher, ob MySQL die Funktion row_number hat. In diesem Fall können Sie das gewünschte Ergebnis erzielen. Unter SQL Server können Sie Folgendes tun:
quelle
Die Lösung von axiac hat am Ende für mich am besten funktioniert. Ich hatte jedoch eine zusätzliche Komplexität: einen berechneten "Maximalwert", abgeleitet aus zwei Spalten.
Verwenden wir das gleiche Beispiel: Ich möchte die älteste Person in jeder Gruppe. Wenn es Menschen gibt, die gleich alt sind, nehmen Sie die größte Person.
Ich musste den linken Join zweimal ausführen, um dieses Verhalten zu erhalten:
Hoffe das hilft! Ich denke, es sollte einen besseren Weg geben, dies zu tun ...
quelle
Meine Lösung funktioniert nur, wenn Sie nur eine Spalte abrufen müssen. Für meine Anforderungen wurde jedoch die beste Lösung in Bezug auf die Leistung gefunden (es wird nur eine einzige Abfrage verwendet!):
Es wird GROUP_CONCAT verwendet, um eine geordnete Concat-Liste zu erstellen, und dann wird nur die erste Teilzeichenfolge erstellt.
quelle
Ich habe eine einfache Lösung mit
WHERE IN
quelle
Verwenden von CTEs - Allgemeine Tabellenausdrücke:
quelle
In Oracle unten kann die Abfrage das gewünschte Ergebnis liefern.
quelle
quelle
Sie können es auch versuchen
quelle
Ich würde Group nicht als Spaltennamen verwenden, da es sich um ein reserviertes Wort handelt. Das folgende SQL würde jedoch funktionieren.
quelle
Diese Methode bietet den Vorteil, dass Sie nach einer anderen Spalte sortieren und die anderen Daten nicht in den Papierkorb werfen können. Dies ist sehr nützlich in Situationen, in denen Sie versuchen, Bestellungen mit einer Spalte für Artikel aufzulisten, wobei die schwersten zuerst aufgelistet werden.
Quelle: http://dev.mysql.com/doc/refman/5.0/de/group-by-functions.html#function_group-concat
quelle
Lass den Tabellennamen Menschen sein
quelle
Wenn ID (und alle Coulmns) von mytable benötigt wird
quelle
Auf diese Weise erhalte ich die N max Zeilen pro Gruppe in MySQL
wie es funktioniert:
co.country = ci.country
) < 1
so für 3 Elemente gesteuert -) <3co.id < ci.id
Vollständiges Beispiel hier:
mysql wähle n max Werte pro Gruppe
quelle