- 1 Section
- 10 Lessons
- unbegrenzt
- SQL Grundlagen10
- 1.1SQL-Grundstruktur: DDL, DML, DQL, DCL
- 1.2CREATE TABLE, Datentypen, Constraints
- 1.3INSERT, UPDATE, DELETE
- 1.4SELECT: WHERE, ORDER BY, LIMIT
- 1.5Aggregatfunktionen: COUNT, SUM, AVG
- 1.6GROUP BY und HAVING
- 1.7INNER JOIN und LEFT JOIN
- 1.8Datenbanktypen im Überblick
- 1.9MySQL / MariaDB in der Praxis
- 1.10SQL-Grundlagenaufgaben
GROUP BY und HAVING
In der vorigen Lektion haben wir mit Aggregatfunktionen ganze Tabellen zu einer Zahl verdichtet. Aber häufig will man nicht einen Wert für alle, sondern einen pro Gruppe: Umsatz pro Kunde, Anzahl pro Stadt, durchschnittlicher Preis pro Kategorie. Genau das macht GROUP BY.
Zusammen mit HAVING – der WHERE-Variante für Aggregate – ist GROUP BY das mächtigste Auswertungswerkzeug in SQL. Praktisch jeder Bericht in einer Anwendung („Top-10-Kunden", „Bestseller pro Monat", „Auftragslage pro Vertriebsregion") baut darauf auf. In dieser Lektion siehst du, wie aus einer Liste von Bestellungen eine Gruppenauswertung wird – und wo der wichtige Unterschied zwischen WHERE und HAVING liegt.
1) Das Grundprinzip: aus N Zeilen werden N Gruppen
GROUP BY teilt die Tabelle in Gruppen anhand einer (oder mehrerer) Spalten – und wendet Aggregatfunktionen auf jede Gruppe separat an. Beispiel: 8 Bestellungen werden in 4 Kunden-Gruppen sortiert, und für jede Gruppe wird der Umsatz summiert.
SELECT kunde, SUM(betrag) AS gesamt FROM bestellung GROUP BY kunde; -- Ergebnis (pro Kunde eine Zeile): -- Müller GmbH | 3940 -- Meier AG | 790 -- Schulz oHG | 3420 -- Weber KG | 670
Die wichtigste Regel: jede Spalte im SELECT muss entweder im GROUP BY stehen oder in einer Aggregatfunktion verpackt sein. Das ist die Quelle vieler Anfängerfehler. Schreibst du SELECT kunde, datum, SUM(betrag) ohne datum in GROUP BY, gibt das DBMS einen Fehler – welches Datum sollte denn aus 3 Bestellungen genommen werden?
2) Bucket-Visualizer: GROUP BY in Aktion
Wähle eine Spalte, nach der gruppiert wird, und eine Aggregatfunktion. Du siehst, wie die Eingangszeilen in Buckets sortiert und pro Bucket verdichtet werden:
EINGANG: bestellung (8 Zeilen)
3) Mehrere Gruppierungs-Spalten
GROUP BY akzeptiert mehrere Spalten – dann wird nach jeder Kombination gruppiert. Beispiel: Umsatz pro Kunde und Monat ergibt mehr Zeilen als Umsatz pro Kunde allein:
-- Umsatz pro Kunde und Monat SELECT kunde, EXTRACT(MONTH FROM datum) AS monat, SUM(betrag) AS umsatz FROM bestellung GROUP BY kunde, monat ORDER BY kunde, monat;
Bei N Kunden und 12 Monaten gibt es bis zu N×12 Ergebniszeilen – aber natürlich nur dort, wo tatsächlich Bestellungen vorliegen. Solche „Kreuztabellen" sind das Rückgrat klassischer Business-Reports. Mehr zu Datumsfunktionen wie EXTRACT in MySQL Praxis.
4) HAVING: Filter auf Gruppen
WHERE und HAVING beantworten beide die Frage „welche Zeilen werden behalten?" – aber an unterschiedlichen Stellen im Ablauf:
WHERE
Filtert vor der Gruppierung. Kennt nur Spalten der Originaltabelle, keine Aggregate.
SELECT kunde, SUM(betrag) FROM bestellung WHERE datum >= '2026-01-01' GROUP BY kunde;
→ Behält nur Bestellungen ab 2026, summiert dann pro Kunde.
HAVING
Filtert nach der Gruppierung. Kennt Aggregate, kann auf SUM/COUNT/AVG-Ergebnisse zugreifen.
SELECT kunde, SUM(betrag) FROM bestellung GROUP BY kunde HAVING SUM(betrag) > 5000;
→ Gruppiert pro Kunde, behält dann nur Gruppen mit Gesamtumsatz > 5000.
5) Die Reihenfolge der Verarbeitung im DBMS
Auch wenn du SELECT als erstes schreibst, führt das DBMS die Schritte intern in einer ganz anderen Reihenfolge aus. Wer diese Reihenfolge versteht, schreibt korrekte und effiziente Queries:
1. FROMSchritt 1
Das DBMS lädt die Quelltabelle(n). Bei JOINs werden die Tabellen erst hier verknüpft.
2. WHERESchritt 2
Zeilen werden gefiltert. Aggregate sind hier noch nicht erlaubt – sie wurden noch nicht berechnet.
3. GROUP BYSchritt 3
Die übrigen Zeilen werden in Gruppen sortiert. Pro Gruppe wird intern eine Reihe gebildet.
4. HAVINGSchritt 4
Auf die Gruppen wird gefiltert. Jetzt sind Aggregate verfügbar – COUNT, SUM, AVG.
5. SELECTSchritt 5
Erst jetzt werden die Spalten ausgewählt und Aliase (AS) vergeben. ORDER BY und LIMIT kommen danach.
6) GROUP BY in der Praxis – die Klassiker
Diese Muster siehst du in echten Anwendungen pausenlos. Lerne sie auswendig, dann erkennst du in IHK-Aufgaben sofort, was gefragt ist:
-- 1. Top-Kunden nach Umsatz SELECT kunde, SUM(betrag) AS total FROM bestellung GROUP BY kunde ORDER BY total DESC LIMIT 10; -- 2. Bestellungen pro Tag SELECT datum, COUNT(*) AS anzahl FROM bestellung GROUP BY datum ORDER BY datum; -- 3. Kunden, die mehr als 3 Bestellungen haben SELECT kunde, COUNT(*) AS n FROM bestellung GROUP BY kunde HAVING COUNT(*) > 3; -- 4. Durchschnittspreis pro Produktkategorie SELECT kategorie, AVG(preis) AS avg_preis, COUNT(*) AS anzahl FROM produkt GROUP BY kategorie ORDER BY avg_preis DESC; -- 5. Duplikate finden – Werte, die mehr als einmal vorkommen SELECT email, COUNT(*) AS n FROM kunde GROUP BY email HAVING COUNT(*) > 1;
Besonders nützlich: Beispiel 5 zum Duplikate-Finden. Wenn deine Tabelle eigentlich keine doppelten E-Mails enthalten sollte, aber irgendwie doch welche da sind, findest du sie mit genau dieser Query. Ein Klassiker bei der Datenqualitäts-Prüfung.
7) Häufige Fehler bei GROUP BY
Diese drei Stolperfallen erwischen fast jeden mindestens einmal:
- Spalte im SELECT, aber nicht im GROUP BY:
SELECT kunde, stadt, SUM(betrag) FROM bestellung GROUP BY kunde– Fehler!stadtist weder Aggregat noch in GROUP BY. Lösung: entwederGROUP BY kunde, stadtoderMAX(stadt)als Hack (wenn Stadt pro Kunde eindeutig ist). - WHERE statt HAVING bei Aggregaten:
WHERE SUM(betrag) > 5000ist syntaktisch verboten. Im WHERE existiert SUM noch gar nicht. Lösung:HAVING SUM(betrag) > 5000. - Alias im HAVING/WHERE genutzt:
SELECT SUM(betrag) AS gesamt ... HAVING gesamt > 5000funktioniert in MySQL und PostgreSQL, ist aber nicht Standard-SQL. Sicherer:HAVING SUM(betrag) > 5000.
Moderne MySQL-Versionen (8.0+) sind strenger geworden und melden den ersten Fehler oft mit einem klaren Hinweis. Ältere Versionen (5.x) waren toleranter und lieferten manchmal überraschende Ergebnisse – siehe MySQL Praxis.
8) GROUP BY mit ROLLUP – Zwischensummen
Eine Erweiterung in vielen DBMS: WITH ROLLUP (MySQL/SQL Server) bzw. ROLLUP() (Standard-SQL/PostgreSQL) erzeugt zusätzliche Zeilen mit Teilsummen:
SELECT kunde, SUM(betrag) FROM bestellung GROUP BY kunde WITH ROLLUP; -- Ergebnis: -- Müller GmbH | 3940 -- Meier AG | 790 -- Schulz oHG | 3420 -- Weber KG | 670 -- NULL | 8820 ← Gesamtsumme aller Kunden!
Sehr nützlich für Reports mit Zwischensummen. In der IHK-Prüfung selten verlangt, aber im Berufsalltag praktisch – besonders bei mehreren Gruppierungs-Spalten, wo ROLLUP automatisch Sub-Totalwerte einzieht.
9) GROUP BY ist kein Sortier-Befehl
Ein verbreiteter Irrglaube: „GROUP BY sortiert ja schon, also brauche ich kein ORDER BY". Falsch. GROUP BY gruppiert, sortiert aber nicht garantiert. In MySQL hat es lange unsortiert (alphabetisch) gewirkt, was den Eindruck einer Sortierung erweckte – aber das war ein Implementierungs-Detail, kein Standard. Heute ist auch MySQL sortier-neutral.
Wer ein sortiertes Ergebnis will, hängt ein ORDER BY an: GROUP BY kunde ORDER BY kunde; oder noch besser ORDER BY gesamt DESC;, wenn man nach dem Aggregat-Wert sortieren will (Top-N-Listen).
Zusammenfassung
GROUP BY teilt eine Tabelle in Gruppen anhand einer oder mehrerer Spalten und wendet Aggregatfunktionen pro Gruppe an. Regel: Jede Spalte im SELECT muss entweder im GROUP BY stehen oder aggregiert sein. HAVING filtert die Gruppen nach der Aggregation – dort funktionieren SUM, COUNT, AVG. WHERE filtert die Roh-Zeilen vor der Aggregation und kennt keine Aggregate. Die logische Verarbeitungsreihenfolge im DBMS lautet: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT (anders als die Schreibreihenfolge!). Typische Anwendungen: Top-Kunden, Bestellungen pro Tag, Kunden mit vielen Aufträgen, Durchschnittspreise pro Kategorie, Duplikate-Suche. Häufige Fehler: Spalte im SELECT vergessen in GROUP BY, WHERE statt HAVING bei Aggregaten, Alias-Verwendung im falschen Block. ROLLUP liefert zusätzlich Zwischensummen. GROUP BY sortiert nicht – für sortierte Ergebnisse braucht es ORDER BY.
Verwandte Lektionen: Aggregatfunktionen · SELECT Grundabfragen · JOINs · und mehrWeitere relevante LektionenSQL-GrundstrukturCREATE TABLEWindow FunctionsCTEsSubqueriesDatenbank-ReportsMySQL Praxis
