mehrstufige Aggregation ohne Umwege
Wer Zwischensummen und Gesamtsummen aus mehreren einzelnen Abfragen mit UNION zusammensetzt, loest ein Problem, das GROUP BY ROLLUP bereits eingebaut hat. Mit ROLLUP, CUBE und GROUPING SETS entstehen mehrstufige Aggregationen in einem einzigen Statement, konsistent und performant.
Inhaltsverzeichnis
- 1. Warum GROUP BY ROLLUP mehr kann als einfaches GROUP BY
- 2. ROLLUP: hierarchische Zwischensummen erzeugen
- 3. CUBE: alle Kombinationen von Gruppierungen
- 4. GROUPING SETS: gezielte Kontrolle statt Automatik
- 5. Die GROUPING Funktion: NULL von echten Werten unterscheiden
- 6. Praxisbeispiel: Umsatzbericht mit Region und Produktkategorie
- 7. Performance-Aspekte auf großen Tabellen
- 8. Datenbank-Unterschiede: PostgreSQL, MySQL, SQL Server, Oracle
- 9. Typische Fehler und Vergleichstabelle
- 10. Zusammenfassung
- 11. FAQ
1. Warum GROUP BY ROLLUP mehr kann als einfaches GROUP BY
Ein klassisches Reporting-Problem: Ein Umsatzbericht soll Summen pro Region, pro Region und Monat, sowie eine Gesamtsumme ueber alle Regionen zeigen. Mit reinem GROUP BY braucht man dafuer mehrere Abfragen, die anschliessend per UNION zusammengefuehrt werden, oder eine Anwendung, die die Zwischensummen im Anschluss selbst berechnet. Genau dieses Problem loest GROUP BY ROLLUP direkt in der Datenbank, in einem einzigen Statement, konsistent und ohne doppelte Logik.
ROLLUP ist eine Erweiterung der GROUP BY Klausel, die zusaetzlich zu den normalen Gruppierungsebenen automatisch Zwischensummen und eine Gesamtsumme erzeugt. Das Ergebnis ist eine einzige Ergebnismenge, in der Detailzeilen und Summenzeilen nebeneinander stehen, unterscheidbar ueber NULL-Werte in den Gruppierungsspalten. Wer GROUP BY ROLLUP einmal verstanden hat, verzichtet in Reporting-Abfragen fast immer auf manuelle Summenberechnung in der Anwendungsschicht.
Verwandt mit ROLLUP sind CUBE und GROUPING SETS, die beide im SQL Standard seit SQL:1999 definiert sind und von allen grossen relationalen Datenbanken unterstuetzt werden. Alle drei Erweiterungen bauen auf demselben Prinzip auf: Statt eine feste Gruppierungsebene festzulegen, beschreibt man mehrere Ebenen gleichzeitig, und die Datenbank fuehrt intern mehrere Aggregationsdurchlaeufe zusammen.
2. ROLLUP: hierarchische Zwischensummen erzeugen
GROUP BY ROLLUP erwartet eine geordnete Liste von Spalten und erzeugt Zwischensummen von rechts nach links, also von der detailliertesten zur groebsten Ebene. Bei GROUP BY ROLLUP(region, kategorie) entstehen drei Gruppierungsebenen: Region und Kategorie zusammen, nur Region, und schliesslich die Gesamtsumme ueber alle Zeilen. Die Reihenfolge der Spalten in ROLLUP ist entscheidend, da sie die Hierarchie der Zwischensummen festlegt, nicht die Sortierung des Ergebnisses.
In den Summenzeilen erscheinen NULL-Werte fuer die Spalten, die auf dieser Aggregationsstufe nicht mehr gruppiert werden. Das kann zu Verwechslungen fuehren, wenn die Quelldaten selbst echte NULL-Werte enthalten, etwa eine nicht zugeordnete Kategorie. Dieses Problem loest die spaeter besprochene GROUPING Funktion zuverlaessig, indem sie zwischen einem echten NULL-Wert und einem durch ROLLUP erzeugten Summen-NULL unterscheidet.
-- Basic ROLLUP: subtotals per region, plus grand total
SELECT
region,
kategorie,
SUM(umsatz) AS summe_umsatz
FROM verkaeufe
GROUP BY ROLLUP(region, kategorie)
ORDER BY region NULLS LAST, kategorie NULLS LAST;
-- Result rows include:
-- ('Nord', 'Elektronik', 12000)
-- ('Nord', 'Moebel', 8000)
-- ('Nord', NULL, 20000) -- subtotal for region 'Nord'
-- ('Sued', 'Elektronik', 9500)
-- ('Sued', NULL, 9500) -- subtotal for region 'Sued'
-- (NULL, NULL, 29500) -- grand total
Ein haeufiger Anwendungsfall fuer GROUP BY ROLLUP sind Finanzberichte mit hierarchischer Struktur, etwa Jahr, Quartal, Monat. Mit ROLLUP(jahr, quartal, monat) erzeugt die Datenbank automatisch Zwischensummen pro Jahr und Quartal sowie eine Gesamtsumme, exakt in der Struktur, wie sie in klassischen Pivot-Berichten erwartet wird. Ohne ROLLUP muesste dieselbe Struktur mit vier separaten GROUP BY Abfragen und UNION ALL nachgebildet werden, was fehleranfaelliger und langsamer ist.
3. CUBE: alle Kombinationen von Gruppierungen
Waehrend ROLLUP nur hierarchische Zwischensummen von rechts nach links erzeugt, geht CUBE einen Schritt weiter und liefert alle moeglichen Kombinationen der angegebenen Spalten. Bei GROUP BY CUBE(region, kategorie) entstehen vier Gruppierungsebenen: Region und Kategorie zusammen, nur Region, nur Kategorie, und die Gesamtsumme. Diese zusaetzliche Kombination, nur nach Kategorie ohne Regionsbezug, liefert ROLLUP nicht, weil ROLLUP an die feste Reihenfolge der Spalten gebunden ist.
CUBE eignet sich besonders fuer explorative Analysen, bei denen im Voraus nicht klar ist, welche Dimension fuer die Auswertung relevanter ist. Ein Business-Analyst, der sowohl Summen pro Region als auch Summen pro Produktkategorie unabhaengig voneinander sehen will, bekommt mit CUBE beide Sichten in einem einzigen Ergebnis, statt zwei getrennte Abfragen zu schreiben und manuell zusammenzufuehren.
-- CUBE: every combination of the given columns
SELECT
region,
kategorie,
SUM(umsatz) AS summe_umsatz
FROM verkaeufe
GROUP BY CUBE(region, kategorie);
-- Compared to ROLLUP, CUBE additionally returns:
-- (NULL, 'Elektronik', 21500) -- total per category, across all regions
-- (NULL, 'Moebel', 8000)
Der Nachteil von CUBE gegenueber ROLLUP ist die Anzahl der Ergebniszeilen. Bei n Spalten erzeugt CUBE bis zu 2 hoch n Kombinationen, waehrend ROLLUP nur n plus 1 Zeilen pro Gruppe liefert. Bei drei oder vier Gruppierungsspalten kann das Ergebnis von CUBE schnell unhandlich gross werden, insbesondere wenn die Basistabelle bereits viele unterschiedliche Werte pro Spalte enthaelt. Vor dem Einsatz von CUBE lohnt sich daher immer ein Blick auf die Kardinalitaet der beteiligten Spalten.
4. GROUPING SETS: gezielte Kontrolle statt Automatik
ROLLUP und CUBE folgen festen Mustern, aber nicht jeder Report braucht wirklich alle hierarchischen Zwischensummen oder alle Kombinationen. GROUPING SETS erlaubt es, exakt die Gruppierungsebenen aufzulisten, die man tatsaechlich benoetigt, ohne die von ROLLUP oder CUBE erzwungene Vollstaendigkeit. Das ist besonders nuetzlich, wenn ein Report nur Region-Gesamt und Kategorie-Gesamt braucht, aber keine Detailebene Region plus Kategorie.
Technisch gesehen sind sowohl ROLLUP als auch CUBE nur syntaktische Abkuerzungen fuer bestimmte GROUPING SETS Ausdruecke. ROLLUP(a, b) entspricht GROUPING SETS((a, b), (a), ()), und CUBE(a, b) entspricht GROUPING SETS((a, b), (a), (b), ()). Wer diese Aequivalenz kennt, kann GROUPING SETS gezielt einsetzen, um Zwischenformen zwischen ROLLUP und CUBE zu erzeugen, die keine der beiden Kurzformen direkt abbildet.
-- GROUPING SETS: only the levels actually needed for the report
SELECT
region,
kategorie,
SUM(umsatz) AS summe_umsatz
FROM verkaeufe
GROUP BY GROUPING SETS (
(region, kategorie), -- detail level
(region), -- subtotal per region
() -- grand total, no per-category subtotal needed
);
In der Praxis lohnt sich GROUPING SETS besonders bei Reports mit sehr vielen Spalten, bei denen CUBE eine kombinatorische Explosion an Zeilen erzeugen wuerde, obwohl nur eine Handvoll spezifischer Kombinationen tatsaechlich gebraucht wird. Eine gut gepflegte GROUPING SETS Abfrage ist oft schneller als eine CUBE Abfrage mit anschliessendem Filter auf die relevanten Zeilen, weil die Datenbank von vornherein nur die benoetigten Aggregationsdurchlaeufe plant.
5. Die GROUPING Funktion: NULL von echten Werten unterscheiden
Ein zentrales Problem bei ROLLUP, CUBE und GROUPING SETS ist die Unterscheidung zwischen einem echten NULL-Wert in den Daten und einem NULL-Wert, den die Datenbank fuer eine Summenzeile erzeugt hat. Die Funktion GROUPING(spalte) loest das zuverlaessig: Sie gibt 1 zurueck, wenn die Spalte auf dieser Aggregationsstufe zusammengefasst wurde, und 0, wenn es sich um einen echten Wert aus den Quelldaten handelt, selbst wenn dieser Wert NULL ist.
Damit laesst sich in der SELECT-Liste eine lesbare Bezeichnung fuer Summenzeilen erzeugen, etwa mit einem CASE Ausdruck, der bei GROUPING gleich 1 den Text Gesamt oder Zwischensumme einsetzt. Das ist besonders wichtig, wenn das Ergebnis direkt in einem Dashboard oder Export angezeigt wird, ohne dass die Anwendung selbst zwischen echten NULL-Werten und Aggregations-NULL-Werten unterscheiden muss.
-- GROUPING() distinguishes real NULL from aggregation NULL
SELECT
CASE WHEN GROUPING(region) = 1 THEN 'Gesamt' ELSE region END AS region_label,
CASE WHEN GROUPING(kategorie) = 1 THEN 'Alle Kategorien' ELSE kategorie END AS kategorie_label,
SUM(umsatz) AS summe_umsatz,
GROUPING(region) AS ist_region_summe,
GROUPING(kategorie) AS ist_kategorie_summe
FROM verkaeufe
GROUP BY ROLLUP(region, kategorie);
Die verwandte Funktion GROUPING_ID(), in PostgreSQL, SQL Server und Oracle verfuegbar, kombiniert mehrere GROUPING Aufrufe zu einem einzigen Bitmuster und erleichtert das Filtern nach bestimmten Aggregationsebenen, etwa nur Zeilen mit exakt einer Zwischensumme. In MySQL 8 gibt es keine native GROUPING_ID Funktion, hier muessen mehrere GROUPING Aufrufe manuell kombiniert werden.
6. Praxisbeispiel: Umsatzbericht mit Region und Produktkategorie
Ein vollstaendiges Praxisbeispiel zeigt, wie GROUP BY ROLLUP in einem realistischen Reporting-Szenario eingesetzt wird. Angenommen, ein Unternehmen moechte einen Quartalsbericht, der Umsatz pro Region und Produktkategorie zeigt, mit Zwischensummen pro Region und einer Gesamtsumme am Ende. Zusaetzlich soll der durchschnittliche Bestellwert je Gruppierungsebene mit ausgegeben werden, was in einer einzigen ROLLUP Abfrage problemlos moeglich ist.
Wichtig bei solchen Berichten ist die richtige Sortierung: Da ROLLUP Zwischensummen mit NULL-Werten erzeugt, muss die ORDER BY Klausel explizit festlegen, ob NULL-Werte am Anfang oder am Ende jeder Gruppe stehen sollen, sonst landen Zwischensummen an zufaelliger Stelle im Ergebnis. In PostgreSQL steuert man das mit NULLS LAST, in MySQL und SQL Server ueber einen zusaetzlichen CASE Ausdruck in der ORDER BY Klausel.
-- Complete quarterly report: subtotals per region, average order value
SELECT
CASE WHEN GROUPING(region) = 1 THEN 'GESAMT' ELSE region END AS region,
CASE WHEN GROUPING(kategorie) = 1 THEN 'Zwischensumme' ELSE kategorie END AS kategorie,
SUM(umsatz) AS summe_umsatz,
ROUND(AVG(bestellwert), 2) AS durchschnitt_bestellwert,
COUNT(*) AS anzahl_bestellungen
FROM verkaeufe
WHERE quartal = 'Q2-2026'
GROUP BY ROLLUP(region, kategorie)
ORDER BY GROUPING(region), region, GROUPING(kategorie), kategorie;
Ein wichtiger Hinweis fuer Anwendungsentwickler: WHERE-Bedingungen werden vor der ROLLUP Berechnung angewendet, filtern also die Basisdaten, nicht die erzeugten Summenzeilen. Wer nur bestimmte Summenzeilen anzeigen moechte, etwa nur Region-Summen ohne Detailzeilen, muss dafuer HAVING mit GROUPING kombinieren, da HAVING nach der Aggregation greift und somit auch auf die durch ROLLUP erzeugten Zeilen angewendet werden kann.
7. Performance-Aspekte auf großen Tabellen
Intern fuehrt eine Datenbank GROUP BY ROLLUP haeufig durch, indem sie die Detaildaten einmal sortiert oder hasht und anschliessend mehrere Aggregationsdurchlaeufe ueber dieselbe sortierte Menge legt, statt die Basistabelle mehrfach zu lesen. Das ist deutlich effizienter als mehrere separate GROUP BY Abfragen mit anschliessendem UNION, da die teuerste Operation, das Lesen und Sortieren der Basisdaten, nur einmal ausgefuehrt wird.
Bei CUBE steigt der Aufwand mit der Anzahl der Spalten exponentiell, da jede zusaetzliche Kombination einen weiteren logischen Aggregationsdurchlauf bedeutet. Auf Tabellen mit vielen Millionen Zeilen und mehr als drei oder vier CUBE Spalten kann die Ausfuehrungszeit spuerbar steigen. In solchen Faellen lohnt es sich, mit EXPLAIN den Ausfuehrungsplan zu pruefen und zu vergleichen, ob GROUPING SETS mit einer reduzierten Anzahl an Kombinationen dieselbe fachliche Anforderung mit weniger Aufwand erfuellt.
Ein Index auf den Gruppierungsspalten kann den sortierbasierten Aggregationspfad beschleunigen, insbesondere wenn die Spalten in der ROLLUP Reihenfolge dem Index entsprechen. Bei sehr grossen Faktentabellen im Data-Warehouse-Kontext ist es zudem sinnvoll, vorab aggregierte Materialized Views fuer die haeufigsten ROLLUP Kombinationen zu pflegen, statt die vollstaendige Aggregation bei jeder Anfrage neu zu berechnen.
8. Datenbank-Unterschiede: PostgreSQL, MySQL, SQL Server, Oracle
Die Syntax von ROLLUP, CUBE und GROUPING SETS ist im SQL Standard definiert, wird aber nicht von allen Datenbanken identisch unterstuetzt. PostgreSQL, SQL Server und Oracle implementieren die vollstaendige Standard-Syntax inklusive verschachtelter GROUPING SETS und GROUPING_ID. MySQL unterstuetzt ROLLUP seit Version 5.7 in einer aelteren Syntax mit WITH ROLLUP am Ende der Abfrage, und erst ab MySQL 8.0 auch die Standard-Syntax GROUP BY ROLLUP(...). CUBE und GROUPING SETS fehlen in MySQL bis heute vollstaendig als native Syntax.
Wer CUBE in MySQL benoetigt, muss es manuell ueber mehrere UNION ALL Abfragen mit unterschiedlichen GROUP BY Ebenen nachbauen, was genau die Umstaendlichkeit wiederherstellt, die ROLLUP und CUBE eigentlich vermeiden sollen. Bei portablem SQL, das auf mehreren Datenbanken laufen soll, ist es daher sinnvoll, GROUPING SETS als kleinsten gemeinsamen Nenner zu pruefen und dessen Verfuegbarkeit vorab zu testen, statt sich blind auf CUBE zu verlassen.
| Feature | PostgreSQL | MySQL 8 | SQL Server / Oracle |
|---|---|---|---|
| ROLLUP | GROUP BY ROLLUP(...) | GROUP BY ROLLUP(...) | GROUP BY ROLLUP(...) |
| CUBE | GROUP BY CUBE(...) | nicht verfuegbar | GROUP BY CUBE(...) |
| GROUPING SETS | unterstuetzt | nicht verfuegbar | unterstuetzt |
| GROUPING_ID() | unterstuetzt | nicht verfuegbar | unterstuetzt |
9. Typische Fehler und Vergleichstabelle
Der haeufigste Fehler bei GROUP BY ROLLUP ist die falsche Spaltenreihenfolge. Wer ROLLUP(kategorie, region) statt ROLLUP(region, kategorie) schreibt, bekommt Zwischensummen pro Kategorie statt pro Region, weil ROLLUP von rechts nach links aggregiert. Ein zweiter Fehler ist das Verwechseln von echten NULL-Werten mit Aggregations-NULL-Werten in nachgelagerten Filtern, was zu falschen Ergebnissen fuehrt, wenn ohne die GROUPING Funktion gefiltert wird. Ein dritter Fehler ist der unreflektierte Einsatz von CUBE bei vielen Spalten, was zu einer unkontrollierten Zeilenexplosion fuehren kann.
Die folgende Uebersicht fasst zusammen, welches Werkzeug fuer welchen Anwendungsfall passt.
| Anforderung | Falsches Werkzeug | Richtiges Werkzeug | Begruendung |
|---|---|---|---|
| Hierarchische Zwischensummen | mehrere UNION ALL Abfragen | ROLLUP | Ein Durchlauf, feste Hierarchie |
| Alle Kombinationen pruefen | manuelle Kreuzprodukte | CUBE | Vollstaendige Kombinatorik automatisch |
| Nur ausgewaehlte Ebenen | CUBE plus nachtraeglicher Filter | GROUPING SETS | Weniger Zeilen, gezielter Plan |
| Summenzeile erkennen | IS NULL Pruefung | GROUPING() | Unterscheidet echtes NULL von Summe |
Mironsoft
SQL-Reporting, Datenmodellierung und Query-Optimierung
Reports mit sauberen Aggregationen statt manueller Summenlogik?
Wir bauen Reporting-Abfragen mit ROLLUP, CUBE und GROUPING SETS, die Zwischensummen direkt in der Datenbank berechnen, korrekt performant und wartbar in eurem bestehenden Datenmodell.
Query-Review
Bestehende Reporting-Queries auf ROLLUP/CUBE-Tauglichkeit pruefen
Performance-Tuning
Ausfuehrungsplaene analysieren und Aggregationspfade optimieren
Datenbank-Migration
Portable Aggregations-SQL fuer mehrere Datenbanksysteme entwerfen
10. Zusammenfassung
GROUP BY ROLLUP erzeugt hierarchische Zwischensummen und eine Gesamtsumme in einem einzigen Statement, von der detailliertesten zur groebsten Ebene, exakt in der Reihenfolge der angegebenen Spalten. CUBE liefert alle moeglichen Kombinationen der Gruppierungsspalten und eignet sich fuer explorative Analysen, bringt aber bei vielen Spalten eine exponentiell wachsende Zeilenanzahl mit sich. GROUPING SETS erlaubt die gezielte Auswahl genau der Aggregationsebenen, die tatsaechlich gebraucht werden, und ist technisch die allgemeinste der drei Formen.
Die GROUPING Funktion loest zuverlaessig das Problem, echte NULL-Werte von durch ROLLUP oder CUBE erzeugten Summen-NULL-Werten zu unterscheiden, und ist Voraussetzung fuer lesbare Bezeichnungen in Reports. Bei der Wahl zwischen ROLLUP, CUBE und GROUPING SETS entscheidet vor allem die Frage, wie viele Kombinationen tatsaechlich benoetigt werden, und wie gross die zugrunde liegende Tabelle ist. MySQL bleibt bei CUBE und GROUPING SETS eingeschraenkt, was bei portablem SQL beruecksichtigt werden muss.
GROUP BY ROLLUP und CUBE — Das Wichtigste auf einen Blick
ROLLUP
Hierarchische Zwischensummen von rechts nach links, ideal fuer feste Berichtsstrukturen wie Jahr, Quartal, Monat.
CUBE
Alle Kombinationen der Gruppierungsspalten, gut fuer explorative Analysen, aber exponentiell wachsende Zeilenzahl.
GROUPING SETS
Gezielte Auswahl der benoetigten Aggregationsebenen, oft schneller als CUBE mit nachtraeglichem Filter.
GROUPING()
Unterscheidet echtes NULL von Aggregations-NULL, Grundlage fuer lesbare Summenzeilen in Reports.