Die GROUP BY
-Klausel
GROUP BY
führt Ausgabezeilen, die dieselbe Kombination von Werten in ihrer Elementliste haben, zu einer einzigen Zeile zusammen.Aggregatfunktionen in der Auswahlliste werden auf jede Gruppe einzeln und nicht auf den gesamten Datensatz angewendet.
Wenn die Auswahlliste nur Aggregatspalten enthält oder allgemeiner Spalten, deren Werte nicht von einzelnen Zeilen in der zugrunde liegenden Menge abhängen, ist GROUP BY
optional.Wenn es weggelassen wird, besteht die endgültige Ergebnismenge von aus einer einzelnen Zeile (vorausgesetzt, dass mindestens eine aggregierte Spalte vorhanden ist).
Wenn die Auswahlliste sowohl Aggregatspalten als auch Spalten enthält, deren Werte pro Zeile variieren können, wird die GROUP BY
-Klausel obligatorisch.
SELECT ... FROM ... GROUP BY <grouping-item> [, <grouping-item> ...] [HAVING <grouped-row-condition>] ... <grouping-item> ::= <non-aggr-select-item> | <non-aggr-expression> <non-aggr-select-item> ::= column-copy | column-alias | column-position
Argument | Beschreibung |
---|---|
non-aggr-expression |
Jeder nicht aggregierende Ausdruck, der nicht in der |
column-copy |
Eine wörtliche Kopie aus der |
column-alias |
Der Alias aus der |
column-position |
Die Positionsnummer in der |
Als allgemeine Faustregel gilt, dass jedes nicht aggregierte Element in der SELECT
-Liste auch in der GROUP BY
-Liste enthalten sein muss.Sie können dies auf drei Arten tun:
-
Durch wörtliches Kopieren des Artikels aus der Auswahlliste, z.B. “
class
” oder “'D:' || upper(doccode)
”. -
Durch Angabe des Spaltenalias, falls vorhanden.
-
Durch Angabe der Spaltenposition als Ganzzahl literal zwischen 1 und der Anzahl der Spalten.Ganzzahlwerte, die aus Ausdrücken oder Parameterersetzungen resultieren, sind einfach unveränderlich und werden als solche in der Gruppierung verwendet.Sie haben jedoch keine Auswirkung, da ihr Wert für jede Zeile gleich ist.
Note
|
Wenn Sie nach einer Spaltenposition gruppieren, wird der Ausdruck an dieser Position intern aus der Auswahlliste kopiert.Wenn es sich um eine Unterabfrage handelt, wird diese Unterabfrage in der Gruppierungsphase erneut ausgeführt.Das heißt, das Gruppieren nach der Spaltenposition, anstatt den Unterabfrageausdruck in der Gruppierungsklausel zu duplizieren, spart Tastenanschläge und Bytes, aber es ist keine Möglichkeit, Verarbeitungszyklen zu sparen! |
Zusätzlich zu den erforderlichen Elementen kann die Gruppierungsliste auch Folgendes enthalten:
-
Spalten aus der Quelltabelle, die nicht in der Auswahlliste enthalten sind, oder nicht aggregierte Ausdrücke, die auf solchen Spalten basieren.Das Hinzufügen solcher Spalten kann die Gruppen weiter unterteilen.Da sich diese Spalten jedoch nicht in der Auswahlliste befinden, können Sie nicht erkennen, welche aggregierte Zeile welchem Wert in der Spalte entspricht.Wenn Sie also an diesen Informationen interessiert sind, nehmen Sie im Allgemeinen auch die Spalte oder den Ausdruck in die Auswahlliste auf — was Sie zu der Regel zurückbringt: “Jede nicht aggregierte Spalte in der Auswahlliste muss auch in der Gruppierungsliste”.
-
Ausdrücke, die nicht von den Daten in der zugrunde liegenden Menge abhängig sind, z. Konstanten, Kontextvariablen, einwertige nicht korrelierte Unterauswahlen usw.Dies wird nur der Vollständigkeit halber erwähnt, da das Hinzufügen solcher Elemente völlig sinnlos ist: Sie beeinflussen die Gruppierung überhaupt nicht.“Harmlose aber nutzlose” Elemente wie diese können auch in der Auswahlliste vorkommen, ohne in die Gruppierungsliste kopiert zu werden.
Beispiele
Wenn die Auswahlliste nur aggregierte Spalten enthält, ist GROUP BY
nicht obligatorisch:
select count(*), avg(age) from students
where sex = 'M';
Dadurch wird eine einzelne Zeile zurückgegeben, die die Anzahl der männlichen Studenten und ihr Durchschnittsalter auflistet.Das Hinzufügen von Ausdrücken, die nicht von Werten in einzelnen Zeilen der Tabelle STUDENTS
abhängen, ändert daran nichts:
select count(*), avg(age), current_date from students
where sex = 'M';
Die Zeile enthält jetzt eine zusätzliche Spalte mit dem aktuellen Datum, aber ansonsten hat sich nichts Wesentliches geändert.Eine GROUP BY
-Klausel ist weiterhin nicht erforderlich.
In beiden obigen Beispielen ist es jedoch erlaubt.Das ist vollkommen gültig:
select count(*), avg(age) from students
where sex = 'M'
group by class;
Dadurch wird für jede Klasse mit Jungen eine Zeile zurückgegeben, in der die Anzahl der Jungen und ihr Durchschnittsalter in dieser bestimmten Klasse aufgeführt sind.(Wenn Sie auch das Feld current_date
belassen, wird dieser Wert in jeder Zeile wiederholt, was nicht sehr aufregend ist.)
Die obige Abfrage hat jedoch einen großen Nachteil: Sie gibt Ihnen Informationen über die verschiedenen Klassen, aber sie sagt Ihnen nicht, welche Zeile für welche Klasse gilt.Um diese zusätzlichen Informationen zu erhalten, muss die nicht aggregierte Spalte "CLASS" zur Auswahlliste hinzugefügt werden:
select class, count(*), avg(age) from students
where sex = 'M'
group by class;
Jetzt haben wir eine nützliche Abfrage.Beachten Sie, dass das Hinzufügen der Spalte CLASS
auch die GROUP BY
-Klausel obligatorisch macht.Wir können diese Klausel nicht mehr löschen, es sei denn, wir entfernen auch CLASS
aus der Spaltenliste.
Die Ausgabe unserer letzten Abfrage kann etwa so aussehen:
CLASS | COUNT | AVG |
---|---|---|
2A |
12 |
13.5 |
2B |
9 |
13.9 |
3A |
11 |
14.6 |
3B |
12 |
14.4 |
… |
… |
… |
Die Überschriften “COUNT” und “AVG” sind wenig aussagekräftig.In einem einfachen Fall wie diesem kommen Sie vielleicht damit durch, aber im Allgemeinen sollten Sie Aggregatspalten einen aussagekräftigen Namen geben, indem Sie sie mit einem Alias versehen:
select class,
count(*) as num_boys,
avg(age) as boys_avg_age
from students
where sex = 'M'
group by class;
Wie Sie sich vielleicht an der formalen Syntax der Spaltenliste erinnern, ist das Schlüsselwort AS
optional.
Das Hinzufügen weiterer nicht-aggregierter (oder besser: zeilenabhängiger) Spalten erfordert auch das Hinzufügen dieser zur GROUP BY
-Klausel.Zum Beispiel möchten Sie vielleicht die oben genannten Informationen auch für Mädchen sehen;und vielleicht möchten Sie auch zwischen Internats- und Tagesschülern unterscheiden:
select class,
sex,
boarding_type,
count(*) as number,
avg(age) as avg_age
from students
group by class, sex, boarding_type;
Dies kann zu folgendem Ergebnis führen:
CLASS | SEX | BOARDING_TYPE | NUMBER | AVG_AGE |
---|---|---|---|---|
2A |
F |
BOARDING |
9 |
13.3 |
2A |
F |
DAY |
6 |
13.5 |
2A |
M |
BOARDING |
7 |
13.6 |
2A |
M |
DAY |
5 |
13.4 |
2B |
F |
BOARDING |
11 |
13.7 |
2B |
F |
DAY |
5 |
13.7 |
2B |
M |
BOARDING |
6 |
13.8 |
… |
… |
… |
… |
… |
Jede Zeile in der Ergebnismenge entspricht einer bestimmten Kombination der Spalten CLASS
, SEX
und BOARDING_TYPE
.Die aggregierten Ergebnisse – Anzahl und Durchschnittsalter – werden für jede dieser eher spezifischen Gruppen einzeln angegeben.In einer Abfrage wie dieser sehen Sie keine Gesamtsumme für Jungen als Ganzes oder Tagesschüler als Ganzes.Das ist der Kompromiss: Je mehr nicht aggregierte Spalten Sie hinzufügen, desto mehr können Sie sehr spezifische Gruppen lokalisieren, aber desto mehr verlieren Sie auch den Überblick.Natürlich können Sie die “gröberen” Aggregate weiterhin durch separate Abfragen erhalten.