Zusammenfassung

Aggregatfunktionen fassen mehrere Tupel zu einem einzigen Ergebniswert zusammen (COUNT, SUM, AVG, MIN, MAX) und werden meist mit GROUP BY kombiniert, um Berechnungen pro Gruppe durchzufuehren. HAVING filtert Gruppen nach der Aggregation, waehrend WHERE einzelne Zeilen vor der Gruppierung filtert. Zentraler Merkpunkt: Die meisten DBMS ignorieren NULL-Werte in Aggregatfunktionen, wobei COUNT(*) alle Zeilen zaehlt und COUNT(spalte) nur Zeilen mit NOT NULL-Wert.

Kernkonzepte

COUNT(*) vs COUNT(spalte) vs COUNT(DISTINCT spalte)

COUNT(*) zaehlt alle Tupel einer Gruppe inklusive Zeilen, die ausschliesslich NULL-Werte enthalten. COUNT(spalte) zaehlt nur diejenigen Zeilen, bei denen die angegebene Spalte einen definierten Wert ungleich NULL hat. COUNT(DISTINCT spalte) zaehlt die Anzahl unterschiedlicher, nicht-NULL Auspraegungen einer Spalte, was zur Duplikate-Eliminierung dient. Diese drei Varianten liefern in der Regel unterschiedliche Zahlen und werden gerne in Multiple-Choice-Fragen verwechselt.

Stern zaehlt Zeilen, Spalte zaehlt Werte, DISTINCT zaehlt Auspraegungen - NULL zaehlt nur der Stern mit.

NULL-Behandlung in Aggregaten

Die meisten DBMS ignorieren NULL-Werte in Aggregatfunktionen wie SUM, AVG, MIN, MAX und COUNT(spalte). Ein AVG(note) rechnet nur den Durchschnitt der nicht-NULL Noten, wodurch der Nenner kleiner sein kann als die Gesamtzahl der Tupel. Bei einer Spalte, die ausschliesslich NULL enthaelt, liefert SUM/AVG/MIN/MAX ebenfalls NULL zurueck, nur COUNT(*) liefert die Zeilenzahl. Diese Semantik weicht bewusst von einfachen Rechenoperationen ab, weil NULL als unbekannt statt als 0 interpretiert wird.

NULL wird in Aggregaten uebersprungen - AVG mittelt nur was da ist, nicht was fehlt.

GROUP BY - Gruppierung

GROUP BY bildet Gruppen aus Tupeln, die in einer oder mehreren Spalten gleiche Werte haben, und liefert pro Gruppe genau ein Ergebnistupel. Alle in der SELECT-Klausel aufgefuehrten Attribute muessen entweder in der GROUP BY-Klausel stehen oder durch eine Aggregatfunktion umschlossen sein. Ohne diese Regel waere unklar, welcher Wert einer Gruppe in einer nicht aggregierten Spalte ausgegeben werden soll. Die Gruppierung erlaubt Fragen wie Summe pro Professor oder Anzahl pro Semester.

Was nicht gruppiert wird, muss aggregiert werden - sonst Fehler.

HAVING - Filter fuer Gruppen

HAVING enthaelt Filterbedingungen, die nach der Gruppierung auf die entstandenen Gruppen angewendet werden, aehnlich zu WHERE fuer ungruppierte Anfragen. In HAVING duerfen Aggregatfunktionen wie AVG(sws) >= 3 verwendet werden, in WHERE dagegen nicht. Der Unterschied ist zeitlich: WHERE filtert einzelne Zeilen vor GROUP BY, HAVING filtert ganze Gruppen nach GROUP BY. In der Ausfuehrungsreihenfolge steht HAVING an Position 4, nach FROM/JOIN, WHERE und GROUP BY.

WHERE filtert Zeilen vor Gruppierung, HAVING filtert Gruppen danach.

Ausfuehrungsreihenfolge von SQL-Klauseln

SQL wird logisch in folgender Reihenfolge ausgefuehrt: 1. FROM/JOIN, 2. WHERE, 3. GROUP BY, 4. HAVING, 5. SELECT, 6. DISTINCT, 7. ORDER BY, 8. Mengenoperationen. Diese Reihenfolge erklaert, warum Aliase aus SELECT nicht in WHERE genutzt werden koennen, aber in ORDER BY schon. Sie ist zentral fuer das Verstehen komplexer Abfragen mit Aggregatfunktionen. Die Ausfuehrungsreihenfolge unterscheidet sich absichtlich von der Schreibreihenfolge, die mit SELECT beginnt.

FROM-WHERE-GROUP-HAVING-SELECT-DISTINCT-ORDER: erst filtern, dann gruppieren, dann aggregieren.

Die fuenf Standardaggregate

AVG berechnet den arithmetischen Mittelwert nicht-NULL Werte einer numerischen Spalte. SUM addiert alle nicht-NULL Werte, MIN und MAX liefern den kleinsten bzw. groessten definierten Wert. COUNT ist die einzige Aggregatfunktion, die auch mit dem Sternoperator arbeiten kann und dann alle Zeilen zaehlt. Alle Aggregate reduzieren eine Menge auf einen Skalarwert und koennen per AS umbenannt werden, zum Beispiel SUM(sws) AS deputat.

AVG, SUM, MIN, MAX, COUNT - fuenf Aggregate reduzieren Mengen zu einem Wert.

Skalare Unterabfrage mit Aggregat

Ein haeufiges Muster ist die skalare Unterabfrage, die genau einen Wert per Aggregat liefert und in WHERE mit einem Attribut verglichen wird. Ein typisches Beispiel ist WHERE semester = (SELECT MAX(semester) FROM studenten), um den Studenten mit den meisten Semestern zu ermitteln. Wichtig: Die Unterabfrage muss genau ein Tupel liefern, sonst gibt es einen Laufzeitfehler. Dies loest das klassische Problem, dass MAX allein nur den Wert, aber nicht die zugehoerige Zeile liefert.

MAX kennt nur den Wert - fuer die Zeile braucht es eine Unterabfrage.

AS-Alias fuer Aggregatspalten

Ergebnisse von Aggregatfunktionen bekommen ohne AS-Klausel oft einen unlesbaren automatischen Spaltennamen wie SUM(sws). Mit AS deputat wird die Spalte klar benannt und kann in ORDER BY (nicht aber in HAVING oder WHERE der gleichen Anfrage) referenziert werden. Dies dient der Lesbarkeit und erleichtert die Weiterverwendung in Programmen. Der AS-Operator kann auch Relationen umbenennen, zum Beispiel FROM studenten AS s.

Ohne AS bleibt SUM(sws) - mit AS wird deputat draus.

Wichtige Details

  • COUNT(*) zaehlt alle Zeilen, auch solche mit ausschliesslich NULL-Werten
  • COUNT(spalte) ignoriert Zeilen mit NULL in dieser Spalte
  • COUNT(DISTINCT spalte) zaehlt nur unterschiedliche nicht-NULL Werte
  • SUM, AVG, MIN, MAX ignorieren NULL-Werte in den meisten DBMS
  • AVG auf Spalte mit NULL-Werten berechnet den Mittelwert der definierten Werte, nicht der Gesamtzeilen
  • Alle nicht aggregierten Attribute in SELECT muessen in GROUP BY vorkommen
  • HAVING kann Aggregate nutzen (HAVING AVG(sws) >= 3), WHERE nicht
  • Ausfuehrungsreihenfolge: FROM, WHERE, GROUP BY, HAVING, SELECT, DISTINCT, ORDER BY
  • MAX allein liefert nur den Wert, nicht den zugehoerigen Datensatz - dafuer braucht es eine Unterabfrage
  • MIN/MAX profitieren stark von Indizes auf der Zielspalte
  • Skalare Unterabfrage muss genau ein Tupel liefern - sonst Laufzeitfehler
  • Aggregate ohne GROUP BY liefern ein einziges Ergebnistupel ueber die gesamte Relation

Beispiele

Durchschnittliche Semesterzahl aller Studenten

Einfaches Aggregat ueber alle Tupel ohne Gruppierung. Ein einzelner Skalarwert wird zurueckgegeben.

SELECT AVG(semester)
  FROM studenten;

Ergebnis: eine Zeile, eine Spalte, z.B. 7.3

Lehrdeputat pro Professor mit Sortierung

Kombination aus GROUP BY und SUM: Fuer jeden Professor wird die Summe der SWS ueber alle gehaltenen Vorlesungen gebildet. ORDER BY DESC bringt den mit dem hoechsten Deputat an die Spitze.

SELECT gelesenvon, SUM(sws) AS deputat
  FROM vorlesungen
 GROUP BY gelesenvon
 ORDER BY deputat DESC;

Ergebnis:
2125 | 10
2137 | 8

HAVING - hochrangige Professoren mit Mindest-SWS

Vollstaendiges Beispiel mit JOIN, WHERE-artiger ON-Bedingung, GROUP BY und HAVING. Nur Gruppen (Prof + Name), deren Durchschnitts-SWS mindestens 3 betraegt, werden ausgegeben.

SELECT gelesenvon, name, SUM(sws) AS deputat
  FROM vorlesungen
 INNER JOIN professoren
    ON gelesenvon = persnr
   AND rang = 'C4'
 GROUP BY gelesenvon, name
HAVING AVG(sws) >= 3;

Ablauf: FROM+JOIN -> WHERE/ON-Filter -> GROUP BY -> HAVING-Filter -> SELECT

COUNT DISTINCT und NULL

Vergleich der drei COUNT-Varianten. Bei einer Tabelle mit 10 Zeilen, davon 3 mit NULL im Semester und Werte 1,2,2,3,3,5,5 in den restlichen 7 Zeilen: COUNT(*) = 10, COUNT(semester) = 7, COUNT(DISTINCT semester) = 4.

SELECT COUNT(*)            AS alle,       -- 10
       COUNT(semester)     AS gefuellt,   -- 7
       COUNT(DISTINCT semester) AS unique  -- 4
  FROM studenten;

MAX per Unterabfrage - Wer hat die meisten Semester?

Klassisches Muster: MAX() allein liefert nur den Wert 18, aber nicht die zugehoerige Zeile. Mit skalarer Unterabfrage wird auf den Maximalwert gefiltert und der komplette Datensatz zurueckgegeben.

SELECT matrnr, name
  FROM studenten
 WHERE semester = (SELECT MAX(semester)
                     FROM studenten);

Visualisierung: GROUP BY Pipeline

Schritt 1: Ausgangstabelle Vorlesungen vorlnr titel sws gelesenvon 5001 Grundzuege 4 2137 5041 Ethik 4 2125 5043 Erkenntnistheorie 3 2126 5049 Mass und Messen 2 2125 5052 Wissenschaftstheorie 3 2126 GROUP BY gelesenvon Schritt 2: Gruppierung in Bloecke pro Professor Prof 2137 Sokrates sws: 4 1 Vorlesung Prof 2125 Kant sws: 4, 2 2 Vorlesungen Prof 2126 Russel sws: 3, 3 2 Vorlesungen SUM(sws) pro Gruppe Schritt 3: Aggregat-Ergebnistabelle gelesenvon deputat 2137 4 2125 6 2126 6 Ausfuehrungsreihenfolge 1 FROM / JOIN 2 WHERE Zeilenfilter 3 GROUP BY Gruppierung 4 HAVING Gruppenfilter 5 SELECT Aggregate 6 DISTINCT 7 ORDER BY Alias aus SELECT funktioniert erst ab ORDER BY. Legende Sokrates (2137) Kant (2125) Russel (2126) Merke: HAVING filtert Gruppen nach der Aggregation, WHERE filtert Zeilen davor.

Haeufige Fragen

Was ist der Unterschied zwischen COUNT(*) und COUNT(spalte)?
COUNT(*) zaehlt jede Zeile, unabhaengig vom Inhalt. COUNT(spalte) zaehlt nur Zeilen, in denen die angegebene Spalte einen nicht-NULL Wert hat. Deshalb kann COUNT(*) groesser oder gleich COUNT(spalte) sein, aber nie kleiner.
Warum darf ich in WHERE keine Aggregatfunktion verwenden?
Weil WHERE vor GROUP BY ausgefuehrt wird. Zum Zeitpunkt der WHERE-Auswertung sind noch keine Gruppen gebildet, also gibt es keine Aggregatwerte. Aggregatbedingungen gehoeren in HAVING, das nach GROUP BY ausgefuehrt wird.
Wie behandeln Aggregatfunktionen NULL-Werte?
Die meisten DBMS ignorieren NULL-Werte in AVG, SUM, MIN, MAX und COUNT(spalte). Nur COUNT(*) zaehlt auch Zeilen, die ausschliesslich NULL enthalten. Ein AVG ueber eine Spalte mit NULL-Werten liefert also den Durchschnitt der definierten Werte.
Was passiert bei nicht-aggregierten Spalten ohne GROUP BY-Eintrag?
Die meisten DBMS werfen einen Fehler, weil pro Gruppe genau ein Ergebnistupel erzeugt wird und unklar ist, welchen Wert die nicht aggregierte, nicht gruppierte Spalte annehmen soll.
Wie finde ich den Datensatz mit dem Maximum eines Attributs?
Mit einer skalaren Unterabfrage: SELECT * FROM tabelle WHERE spalte = (SELECT MAX(spalte) FROM tabelle). MAX() allein liefert nur den Wert, nicht die zugehoerige Zeile.
Was macht DISTINCT innerhalb von COUNT?
COUNT(DISTINCT spalte) eliminiert Duplikate vor dem Zaehlen und liefert die Anzahl unterschiedlicher Auspraegungen. NULL-Werte werden weiterhin nicht mitgezaehlt.
Kann ich einen AS-Alias in HAVING verwenden?
In der Regel nein, da SELECT logisch nach HAVING ausgefuehrt wird. Man muss den Aggregatausdruck in HAVING erneut hinschreiben, zum Beispiel HAVING SUM(sws) > 5 statt HAVING deputat > 5. In ORDER BY funktioniert der Alias hingegen.
Was ist die Ausfuehrungsreihenfolge einer SQL-Anfrage mit Aggregatfunktion?
FROM/JOIN, dann WHERE, dann GROUP BY, dann HAVING, dann SELECT (inklusive Aggregatberechnung), dann DISTINCT, dann ORDER BY, zuletzt Mengenoperationen wie UNION.

Pruefungsfragen

0 von 12 Fragen beantwortet