Alle Tabs der Lerneinheit (Erklärung · Interaktiv · Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).
Erklärung
GROUP BY fasst Zeilen mit gleichem Wert in einer oder mehreren Spalten zu Gruppen zusammen, dann berechnest du pro Gruppe ein Aggregat (COUNT, SUM, AVG, MIN, MAX). Statt Einzelzeilen siehst du Zusammenfassungen.
Was du in der Klausur können musst:
- COUNT(*): zählt Zeilen pro Gruppe
- SUM(spalte): summiert numerische Werte pro Gruppe
- AVG(spalte): Durchschnitt pro Gruppe
- MIN / MAX: Extremwerte pro Gruppe
- HAVING: Filter auf das Aggregat-Ergebnis (WHERE filtert Zeilen, HAVING filtert Gruppen)
Die sichere Klausurregel: jede nicht-aggregierte Spalte aus SELECT muss in GROUP BY stehen, sonst Syntaxfehler. Manche DBs (PostgreSQL, MySQL mit ONLY_FULL_GROUP_BY) erlauben Ausnahmen, wenn die Spalte funktional abhängig von einer Gruppierungsspalte ist (z.B. nach Primärschlüssel gruppiert), in der Klausur trotzdem alle nicht-aggregierten Spalten in GROUP BY aufnehmen. Reihenfolge der Klauseln: SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY.
Das Problem
Du hast eine Tabelle mit 1.000 Verkäufen. Frage: "Wieviel Umsatz pro Kategorie?"
verkaeufe
┌────┬───────┬───────────────┬──────────┐
│ id │ kunde │ kategorie │ betrag │
├────┼───────┼───────────────┼──────────┤
│ 1 │ Anna │ Elektronik │ 1200 │
│ 2 │ Anna │ Elektronik │ 25 │
│ 3 │ Ben │ Bücher │ 30 │
│ ...│ ... │ ... │ ... │
└────┴───────┴───────────────┴──────────┘
Mit SELECT * siehst du 1000 Zeilen. Du brauchst aber eine Zahl pro Kategorie.
GROUP BY fasst Zeilen zu Gruppen zusammen. Plus eine Aggregat-Funktion (SUM, COUNT, AVG, MIN, MAX) pro Gruppe.
SELECT kategorie, SUM(betrag)
FROM verkaeufe
GROUP BY kategorie;
Result:
kategorie | SUM(betrag)
─────────── | ───────────
Elektronik | 1915
Bücher | 55
Möbel | 1350
3 Zeilen aus 1000, pro Kategorie eine.
Die 5 wichtigsten Aggregat-Funktionen
| Funktion | Was sie tut | Beispiel |
|---|---|---|
| COUNT(*) | Zählt Zeilen | COUNT(*) → 9 |
| COUNT(spalte) | Zählt nicht-NULL Werte | COUNT(email) |
| SUM(spalte) | Summe (numerisch) | SUM(betrag) → 3320 |
| AVG(spalte) | Mittelwert | AVG(betrag) → 369 |
| MIN(spalte) | Kleinster Wert | MIN(betrag) → 25 |
| MAX(spalte) | Größter Wert | MAX(betrag) → 1200 |
Achtung NULL: SUM, AVG, MIN, MAX ignorieren NULLs.
COUNT(*)zählt aber alle Zeilen,COUNT(spalte)nur Zeilen mit nicht-NULL Wert in dieser Spalte. Klausur-Klassiker.
GROUP BY Schritt für Schritt
SELECT kunde, COUNT(*) FROM verkaeufe GROUP BY kunde;
Was passiert intern:
- FROM: Tabelle laden
- WHERE: Zeilen filtern (falls vorhanden)
- GROUP BY: Zeilen mit gleichem
kundezusammenfassen → "Buckets" - Aggregat: pro Gruppe COUNT/SUM/etc berechnen
- HAVING: Gruppen filtern (falls vorhanden)
- SELECT: Spalten ausgeben
- ORDER BY: sortieren
Vor GROUP BY: Nach GROUP BY (kunde):
Anna, Laptop, 1200 Anna → [Laptop, Maus, Sofa] → COUNT = 3
Anna, Maus, 25 Ben → [Buch, Lampe, Kopfhörer] → COUNT = 3
Ben, Buch, 30 Clara → [Tablet, Buch] → COUNT = 2
Clara, Tablet, 600 David → [Tisch] → COUNT = 1
Ben, Lampe, 80
Anna, Sofa, 950
Clara, Buch, 25
David, Tisch, 320
Ben, Kopfhörer, 90
Die wichtige Regel: SELECT-Spalten
Im SELECT dürfen nur Spalten stehen, die entweder im GROUP BY sind ODER ein Aggregat drumherum haben.
-- OK: kunde ist im GROUP BY, COUNT ist Aggregat
SELECT kunde, COUNT(*) FROM verkaeufe GROUP BY kunde;
-- OK: beide im GROUP BY
SELECT kunde, kategorie FROM verkaeufe GROUP BY kunde, kategorie;
-- FEHLER: produkt nicht im GROUP BY und nicht aggregiert
SELECT kunde, produkt FROM verkaeufe GROUP BY kunde;
Warum? Bei GROUP BY werden Zeilen zusammengefasst.
produkthat aber mehrere Werte pro Gruppe, die DB weiß nicht welcher angezeigt werden soll.
HAVING vs WHERE
Beide filtern, aber an unterschiedlicher Stelle:
| Klausel | Wann | Auf was |
|---|---|---|
| WHERE | VOR GROUP BY | einzelne Zeilen |
| HAVING | NACH GROUP BY | aggregierte Gruppen |
-- Welcher Kunde hat Gesamtumsatz > 1000 €?
SELECT kunde, SUM(betrag)
FROM verkaeufe
GROUP BY kunde
HAVING SUM(betrag) > 1000;
Klausur-Trick: HAVING braucht das Aggregat im Vergleich, WHERE NICHT.
-- FALSCH: SUM ist nicht in WHERE erlaubt (Aggregat existiert noch nicht)
WHERE SUM(betrag) > 1000
-- RICHTIG:
HAVING SUM(betrag) > 1000
-- Auch RICHTIG: WHERE auf einzelne Zeilen
WHERE betrag > 100 -- filtert Zeilen vor der Gruppierung
Mehrere Spalten im GROUP BY
SELECT kategorie, kunde, SUM(betrag)
FROM verkaeufe
GROUP BY kategorie, kunde
ORDER BY kategorie, kunde;
Erzeugt eine Kombinations-Gruppierung: pro (kategorie, kunde)-Paar eine Zeile.
Klausur-Tricks
Trick 1, Reihenfolge der Klauseln auswendig:
SELECT
FROM
WHERE -- vor GROUP BY (Zeilen)
GROUP BY
HAVING -- nach GROUP BY (Aggregate)
ORDER BY
Trick 2, SELECT-Regel: nur GROUP BY-Spalten ODER Aggregate. Klausur prüft das gerne mit Trickfragen.
Trick 3, COUNT(*) vs COUNT(spalte):
- COUNT(*) = alle Zeilen
- COUNT(email) = Zeilen mit nicht-NULL email
- COUNT(DISTINCT kunde) = unterschiedliche Kunden
Trick 4, AVG bei NULLs: ignoriert NULLs. AVG(note) über 10 Studis wo 2 keine Note haben → Mittelwert über 8 Werte.
Trick 5, HAVING ohne GROUP BY: technisch erlaubt, dann gilt die ganze Tabelle als eine Gruppe. Selten gebraucht.
Trick 6, ORDER BY mit Aggregat:
SELECT kunde, SUM(betrag)
FROM verkaeufe
GROUP BY kunde
ORDER BY SUM(betrag) DESC; -- Top-Kunden zuerst
Trick 7, DISTINCT vs GROUP BY: für reines "unterschiedliche Werte zeigen" liefern beide dasselbe Ergebnis:
SELECT DISTINCT kategorie FROM verkaeufe;
SELECT kategorie FROM verkaeufe GROUP BY kategorie;
-- gleicher Output
Die interne Optimierung ist DB-abhängig (manche DBs planen identisch, andere nicht). GROUP BY ist mächtiger, sobald Aggregate dazukommen.
Trick 8, Alias in HAVING (dialektabhängig):
SELECT kunde, SUM(betrag) AS total
FROM verkaeufe
GROUP BY kunde
HAVING total > 1000;
-- ✅ MySQL / SQLite: erlaubt
-- ❌ PostgreSQL und Standard-SQL: nicht erlaubt (Alias erst nach SELECT verfügbar)
Sicherer Standard (in allen DBs):
HAVING SUM(betrag) > 1000;
Trick 9, Leere Ergebnismenge: COUNT(*) → 0, SUM/AVG/MIN/MAX → NULL (Standard-SQL, auch PostgreSQL und SQLite). Wenn 0 statt NULL gewünscht ist: COALESCE(SUM(betrag), 0) benutzen. (Nur SQLites total() liefert direkt 0.0 statt NULL, sum() hingegen NULL.)
Trick 10, GROUPING SETS / ROLLUP / CUBE: erweiterte Gruppierungen für Reporting (Master-Stoff). Erstmal ignorieren.
Wo brauchst du GROUP BY?
- Reports / Dashboards: Umsatz pro Monat, User pro Region
- Statistiken: Durchschnittsnote pro Klausur, Anzahl Bestellungen pro Kunde
- Data Warehouses: jedes BI-Tool macht GROUP BY im Hintergrund
- Web-Analytics: Pageviews pro URL, Sessions pro Browser
- Logs analysieren: Fehler pro Stunde, Status-Codes pro Endpoint
Faustregel: wenn die Frage "wieviel/wieviele/durchschnittlich pro X" lautet → GROUP BY.
Interaktiv
GROUP BY-Lab
Tabelle verkaeufe mit 9 Zeilen. Wähle:
- GROUP BY: kunde oder kategorie
- Aggregat: COUNT, SUM, AVG, MIN, MAX
- HAVING: optional Filter auf das Aggregat
Die Quell-Tabelle oben markiert in Akzent-Tönung welche Zeilen ins Result einfließen, bei HAVING-Filter werden ganze Gruppen ausgegraut.
Probier folgendes:
- GROUP BY kunde, COUNT → Anzahl Verkäufe pro Kunde
- GROUP BY kategorie, SUM → Umsatz pro Kategorie
- GROUP BY kunde, SUM, HAVING > 1000 → nur "wertvolle" Kunden
- GROUP BY kategorie, AVG → durchschnittlicher Verkaufs-Betrag pro Kategorie
Interaktive Visualisierung
GROUP BY mit Aggregations-Funktionen (COUNT, SUM, AVG, MIN, MAX) und HAVING-Filter.
Faustregel zum Mitnehmen: GROUP BY teilt die Tabelle in Gruppen, das Aggregat macht eine Zahl pro Gruppe. HAVING filtert auf den Aggregaten, WHERE auf den einzelnen Zeilen.
Quiz
Klausurfragen mit Lösungen (8)
- F1.Welche Aggregat-Funktion zählt alle Zeilen einer Gruppe (auch mit NULLs)?
Antwort: COUNT(*)
Erklärung: COUNT(*) zählt jede Zeile, egal ob NULL drin. COUNT(spalte) zählt nur Zeilen mit nicht-NULL-Wert. SUM ist Summe, TOTAL existiert nicht in Standard-SQL.
- F2.Was ist der Unterschied zwischen WHERE und HAVING?
Antwort: WHERE filtert vor GROUP BY (auf Zeilen), HAVING danach (auf Aggregaten)
Erklärung: WHERE wird VOR der Gruppierung angewendet, filtert einzelne Zeilen. HAVING NACH, filtert Gruppen mit Aggregaten. Klausur-Klassiker. Faustregel: SUM/COUNT/AVG → HAVING. einfache Spalten-Vergleiche → WHERE.
- F3.Welche Query ist syntaktisch falsch?
Antwort: SELECT kunde, produkt FROM verkaeufe GROUP BY kunde
Erklärung: Antwort C ist falsch: produkt ist weder im GROUP BY noch aggregiert. Bei GROUP BY müssen alle SELECT-Spalten entweder IM GROUP BY oder durch Aggregate-Funktion umschlossen sein. Klausur-Klassiker.
- F4.Du willst Kunden mit Gesamtumsatz > 1000 €. Welche Query?
Antwort: SELECT kunde, SUM(betrag) FROM verkaeufe GROUP BY kunde HAVING SUM(betrag) > 1000
Erklärung: HAVING ist die richtige Klausel für Filter auf Aggregaten. Antwort A ist falsch (SUM in WHERE nicht erlaubt, Aggregat existiert noch nicht). Antwort B ist Reihenfolge-Fehler (WHERE vor GROUP BY). Antwort D filtert auf Einzel-Zeilen statt Gesamtsumme.
- F5.AVG(betrag) bei einer Gruppe mit Werten [100, NULL, 200, NULL, 300]. Was ist das Ergebnis?
Antwort: 200 (Summe ÷ 3, NULLs ignoriert)
Erklärung: Aggregat-Funktionen ignorieren NULLs (AVG, SUM, MIN, MAX, COUNT(spalte)). AVG = (100+200+300)/3 = 200. Wäre NULL als 0 gewertet, wäre AVG = 600/5 = 120, falsche Statistik!
- F6.Was liefert SELECT DISTINCT kategorie FROM verkaeufe und SELECT kategorie FROM verkaeufe GROUP BY kategorie?
Antwort: Identische Ergebnisse, beide entfernen Duplikate
Erklärung: Für dieses einfache Ergebnis liefern beide dieselben Kategorien, Duplikate werden entfernt. Die interne Optimierung ist DB-abhängig (manche DBs planen DISTINCT und GROUP BY identisch, andere nicht). GROUP BY wird mächtiger, sobald Aggregate dazukommen, DISTINCT kann nur Duplikate entfernen.
- F7.In welcher Reihenfolge wird die Query intern ausgewertet?
Antwort: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
Erklärung: Innerlich: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. SELECT steht zwar oben in der Query, wird aber nach GROUP BY und HAVING ausgewertet. Wichtig fürs Verständnis: Aliase aus SELECT sind in WHERE/HAVING manchmal nicht verfügbar.
- F8.Du willst die Top-3 Kunden nach Umsatz. Welche Query?
Antwort: SELECT kunde, SUM(betrag) FROM verkaeufe GROUP BY kunde ORDER BY SUM(betrag) DESC LIMIT 3
Erklärung: Pattern: GROUP BY → ORDER BY (DESC für absteigend) → Begrenzung. **LIMIT 3** ist üblich in PostgreSQL/MySQL/SQLite. **Portabler SQL-Standard:** `FETCH FIRST 3 ROWS ONLY` (auch in PostgreSQL akzeptiert). SQL Server nutzt `SELECT TOP 3 ...`.