ROLLUP, CUBE und GROUPING SETS in Oracle SQL richtig verwenden

Mit ROLLUP, CUBE und GROUPING SETS lassen sich in Oracle SQL mehrere Aggregationsebenen innerhalb einer einzigen Abfrage berechnen. Dadurch können Detailwerte, Zwischensummen und Gesamtsummen gemeinsam ausgegeben werden.

Die drei Erweiterungen verfolgen unterschiedliche Ziele:

  • ROLLUP erzeugt hierarchisch aufgebaute Zwischensummen.
  • CUBE berechnet alle Kombinationen der angegebenen Gruppierungsmerkmale.
  • GROUPING SETS beschränkt die Ausgabe auf ausdrücklich festgelegte Gruppierungen.
  • GROUPING und GROUPING_ID zeigen, zu welcher Aggregationsebene eine Ergebniszeile gehört.

Dieser Artikel erklärt die Unterschiede anhand vollständiger Testdaten und ausführbarer Oracle-SQL-Abfragen.

Testdaten für die Beispiele

Für die folgenden Beispiele wird eine vereinfachte Umsatztabelle verwendet. Sie enthält Umsätze nach Land, Stadt und Produkt.

CREATE TABLE umsatz_daten (
    umsatz_id      NUMBER        NOT NULL,
    buchungsdatum  DATE          NOT NULL,
    land            VARCHAR2(30),
    stadt           VARCHAR2(30),
    produkt         VARCHAR2(50),
    betrag          NUMBER(12, 2) NOT NULL,
    CONSTRAINT pk_umsatz_daten PRIMARY KEY (umsatz_id)
);

Die Testdaten können mit INSERT ALL eingefügt werden:

INSERT ALL
    INTO umsatz_daten
        (umsatz_id, buchungsdatum, land, stadt, produkt, betrag)
    VALUES
        (1, DATE '2026-01-10', 'Österreich', 'Graz', 'Laptop', 1000)

    INTO umsatz_daten
        (umsatz_id, buchungsdatum, land, stadt, produkt, betrag)
    VALUES
        (2, DATE '2026-01-12', 'Österreich', 'Graz', 'Monitor', 500)

    INTO umsatz_daten
        (umsatz_id, buchungsdatum, land, stadt, produkt, betrag)
    VALUES
        (3, DATE '2026-01-15', 'Österreich', 'Wien', 'Laptop', 1500)

    INTO umsatz_daten
        (umsatz_id, buchungsdatum, land, stadt, produkt, betrag)
    VALUES
        (4, DATE '2026-01-20', 'Österreich', 'Wien', 'Monitor', 700)

    INTO umsatz_daten
        (umsatz_id, buchungsdatum, land, stadt, produkt, betrag)
    VALUES
        (5, DATE '2026-01-17', 'Deutschland', 'München', 'Laptop', 1200)

    INTO umsatz_daten
        (umsatz_id, buchungsdatum, land, stadt, produkt, betrag)
    VALUES
        (6, DATE '2026-01-22', 'Deutschland', 'München', 'Monitor', 600)

    INTO umsatz_daten
        (umsatz_id, buchungsdatum, land, stadt, produkt, betrag)
    VALUES
        (7, DATE '2026-01-25', 'Deutschland', 'Berlin', 'Laptop', 900)
SELECT 1
FROM dual;

COMMIT;

Die Tabelle enthält insgesamt einen Umsatz von 6.400.

Nach Ländern ergeben sich:

LandUmsatz
Österreich3.700
Deutschland2.700
Gesamtsumme6.400

Nach Produkten ergeben sich:

ProduktUmsatz
Laptop4.600
Monitor1.800
Gesamtsumme6.400

Normale Aggregation mit GROUP BY

Eine gewöhnliche GROUP BY-Abfrage liefert nur die ausdrücklich angegebene Gruppierungsebene.

SELECT
    land,
    stadt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY
    land,
    stadt
ORDER BY
    land,
    stadt;

Das Ergebnis enthält eine Zeile je Kombination aus Land und Stadt:

LANDSTADTUMSATZ
DeutschlandBerlin900
DeutschlandMünchen1.800
ÖsterreichGraz1.500
ÖsterreichWien2.200

Eine Zwischensumme pro Land oder eine Gesamtsumme wird nicht erzeugt.

Dafür müssten ohne ROLLUP mehrere Abfragen miteinander verbunden werden. Ein typischer, aber aufwendiger Ansatz wäre:

SELECT
    land,
    stadt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY
    land,
    stadt

UNION ALL

SELECT
    land,
    NULL AS stadt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY
    land

UNION ALL

SELECT
    NULL AS land,
    NULL AS stadt,
    SUM(betrag) AS umsatz
FROM umsatz_daten;

ROLLUP erzeugt dieselben Aggregationsebenen wesentlich übersichtlicher.

Zwischensummen mit ROLLUP erzeugen

ROLLUP bildet eine hierarchische Aggregation. Die Gruppierung wird von rechts nach links schrittweise reduziert.

SELECT
    land,
    stadt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY ROLLUP(land, stadt)
ORDER BY
    GROUPING(land),
    land,
    GROUPING(stadt),
    stadt;

Die Abfrage erzeugt drei Ebenen:

  1. Umsatz nach Land und Stadt
  2. Zwischensumme nach Land
  3. Gesamtsumme über alle Datensätze

Das Ergebnis sieht vereinfacht so aus:

LANDSTADTUMSATZ
DeutschlandBerlin900
DeutschlandMünchen1.800
DeutschlandNULL2.700
ÖsterreichGraz1.500
ÖsterreichWien2.200
ÖsterreichNULL3.700
NULLNULL6.400

Die Zeilen mit einer leeren Stadt sind die Zwischensummen der jeweiligen Länder.

Die letzte Zeile, in der sowohl Land als auch Stadt leer sind, ist die Gesamtsumme.

Warum die Reihenfolge bei ROLLUP wichtig ist

Die Reihenfolge der Spalten innerhalb von ROLLUP bestimmt die erzeugte Hierarchie.

GROUP BY ROLLUP(land, stadt)

entspricht inhaltlich folgenden Gruppierungen:

(land, stadt)
(land)
()

Die leeren Klammern stehen für die Gesamtsumme.

Die umgekehrte Reihenfolge:

GROUP BY ROLLUP(stadt, land)

erzeugt dagegen:

(stadt, land)
(stadt)
()

Dabei werden Zwischensummen nach Stadt und nicht nach Land gebildet.

Bei hierarchischen Daten muss deshalb zuerst die übergeordnete und danach die untergeordnete Ebene angegeben werden.

Beispiele:

Jahr, Monat, Tag
Land, Region, Stadt
Abteilung, Team, Mitarbeiter
Produktgruppe, Produkt

Drei Aggregationsebenen mit ROLLUP

ROLLUP kann mehr als zwei Spalten verarbeiten.

SELECT
    land,
    stadt,
    produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY ROLLUP(
    land,
    stadt,
    produkt
)
ORDER BY
    GROUPING(land),
    land,
    GROUPING(stadt),
    stadt,
    GROUPING(produkt),
    produkt;

Diese Abfrage erzeugt:

Land, Stadt und Produkt
Land und Stadt
Land
Gesamtsumme

Für Österreich und Graz entstehen beispielsweise:

LANDSTADTPRODUKTUMSATZ
ÖsterreichGrazLaptop1.000
ÖsterreichGrazMonitor500
ÖsterreichGrazNULL1.500

Danach folgen die weiteren Städte, die Zwischensumme für Österreich und abschließend die Gesamtsumme.

Künstliche NULL-Werte mit GROUPING erkennen

ROLLUP und CUBE verwenden NULL, um zusammengefasste Dimensionen darzustellen.

Ein NULL in einer Summenzeile bedeutet beispielsweise:

Alle Städte
Alle Produkte
Alle Länder

Das ist nicht dasselbe wie ein tatsächlich in der Tabelle gespeicherter NULL-Wert.

Die Funktion GROUPING unterscheidet beide Fälle.

GROUPING(land)

liefert:

0 = normaler Gruppenwert oder tatsächlich gespeichertes NULL
1 = von ROLLUP, CUBE oder GROUPING SETS erzeugte Summierung

Damit lassen sich verständliche Beschriftungen erzeugen:

SELECT
    CASE
        WHEN GROUPING(land) = 1
        THEN 'Alle Länder'
        ELSE NVL(land, 'Land unbekannt')
    END AS land,

    CASE
        WHEN GROUPING(stadt) = 1
        THEN 'Alle Städte'
        ELSE NVL(stadt, 'Stadt unbekannt')
    END AS stadt,

    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY ROLLUP(land, stadt)
ORDER BY
    GROUPING(land),
    land,
    GROUPING(stadt),
    stadt;

Das Ergebnis ist leichter verständlich:

LANDSTADTUMSATZ
DeutschlandBerlin900
DeutschlandMünchen1.800
DeutschlandAlle Städte2.700
ÖsterreichGraz1.500
ÖsterreichWien2.200
ÖsterreichAlle Städte3.700
Alle LänderAlle Städte6.400

Eine Prüfung mit:

land IS NULL

kann nicht unterscheiden, ob der Wert tatsächlich gespeichert wurde oder durch die Aggregation entstanden ist.

Dafür muss GROUPING(land) verwendet werden.

Tatsächliches NULL und Summenzeile unterscheiden

Das folgende Beispiel enthält bewusst einen Datensatz ohne Land:

WITH daten AS (
    SELECT
        CAST(NULL AS VARCHAR2(30)) AS land,
        100 AS betrag
    FROM dual

    UNION ALL

    SELECT
        'Österreich',
        200
    FROM dual
)
SELECT
    CASE
        WHEN GROUPING(land) = 1
        THEN 'Alle Länder'

        WHEN land IS NULL
        THEN 'Land unbekannt'

        ELSE land
    END AS land,
    SUM(betrag) AS betrag
FROM daten
GROUP BY ROLLUP(land);

Das Ergebnis enthält drei unterschiedliche Zeilen:

LANDBETRAG
Land unbekannt100
Österreich200
Alle Länder300

Ohne GROUPING könnten die Zeile „Land unbekannt“ und die Gesamtsumme nicht zuverlässig unterschieden werden.

Alle Kombinationen mit CUBE berechnen

CUBE erzeugt alle möglichen Kombinationen der angegebenen Gruppierungsmerkmale.

SELECT
    land,
    produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY CUBE(land, produkt);

Bei zwei Gruppierungsspalten entstehen vier Aggregationsebenen:

(land, produkt)
(land)
(produkt)
()

Damit liefert die Abfrage:

  1. Umsatz je Land und Produkt
  2. Umsatz je Land
  3. Umsatz je Produkt
  4. Gesamtsumme

Mit verständlichen Beschriftungen:

SELECT
    CASE
        WHEN GROUPING(land) = 1
        THEN 'Alle Länder'
        ELSE land
    END AS land,

    CASE
        WHEN GROUPING(produkt) = 1
        THEN 'Alle Produkte'
        ELSE produkt
    END AS produkt,

    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY CUBE(land, produkt)
ORDER BY
    GROUPING_ID(land, produkt),
    land NULLS LAST,
    produkt NULLS LAST;

Das Ergebnis:

LANDPRODUKTUMSATZ
DeutschlandLaptop2.100
DeutschlandMonitor600
ÖsterreichLaptop2.500
ÖsterreichMonitor1.200
DeutschlandAlle Produkte2.700
ÖsterreichAlle Produkte3.700
Alle LänderLaptop4.600
Alle LänderMonitor1.800
Alle LänderAlle Produkte6.400

Unterschied zwischen ROLLUP und CUBE

Bei zwei Spalten erzeugt ROLLUP drei Gruppierungen:

GROUP BY ROLLUP(land, produkt)

entspricht:

(land, produkt)
(land)
()

Eine produktübergreifende Auswertung ohne Land wird nicht erzeugt.

CUBE erzeugt dagegen vier Gruppierungen:

GROUP BY CUBE(land, produkt)

entspricht:

(land, produkt)
(land)
(produkt)
()

Damit kommt zusätzlich die Aggregation nur nach Produkt hinzu.

ROLLUP eignet sich für Hierarchien. CUBE eignet sich für Dimensionen, die unabhängig voneinander ausgewertet werden sollen.

Aggregationsebenen mit GROUPING_ID erkennen

Bei mehreren Gruppierungsspalten werden mehrere Aufrufe von GROUPING schnell unübersichtlich.

GROUPING(land)
GROUPING(produkt)

GROUPING_ID fasst diese Informationen in einer Zahl zusammen:

GROUPING_ID(land, produkt)

Bei zwei Spalten ergeben sich folgende Werte:

LAND aggregiertPRODUKT aggregiertBitmusterGROUPING_ID
00000
01011
10102
11113

Damit gilt:

0 = Detailgruppe nach Land und Produkt
1 = Zwischensumme nach Land
2 = Zwischensumme nach Produkt
3 = Gesamtsumme

Die Gruppierungsnummer kann direkt ausgegeben werden:

SELECT
    land,
    produkt,
    SUM(betrag) AS umsatz,
    GROUPING_ID(land, produkt) AS gruppierung
FROM umsatz_daten
GROUP BY CUBE(land, produkt)
ORDER BY
    gruppierung,
    land,
    produkt;

Bestimmte Summenebenen filtern

Mit GROUPING_ID können bestimmte Aggregationsebenen gezielt ausgewählt werden.

Nur Zwischensummen nach Land und die Gesamtsumme:

SELECT
    land,
    produkt,
    SUM(betrag) AS umsatz,
    GROUPING_ID(land, produkt) AS gruppierung
FROM umsatz_daten
GROUP BY CUBE(land, produkt)
HAVING GROUPING_ID(land, produkt) IN (1, 3)
ORDER BY
    gruppierung,
    land;

Das Ergebnis:

LANDPRODUKTUMSATZGRUPPIERUNG
DeutschlandNULL2.7001
ÖsterreichNULL3.7001
NULLNULL6.4003

Nur die Gesamtsumme:

HAVING GROUPING_ID(land, produkt) = 3

Alle Summenzeilen, aber keine Detailzeilen:

HAVING GROUPING_ID(land, produkt) > 0

Nur benötigte Gruppierungen mit GROUPING SETS berechnen

Ein vollständiger CUBE erzeugt auch Gruppierungen, die möglicherweise gar nicht benötigt werden.

Angenommen, ein Bericht soll nur diese Ebenen enthalten:

  1. Land und Produkt
  2. Zwischensumme je Land
  3. Gesamtsumme

Eine reine Produktaggregation wird nicht benötigt.

Dafür eignet sich GROUPING SETS:

SELECT
    land,
    produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY GROUPING SETS (
    (land, produkt),
    (land),
    ()
)
ORDER BY
    GROUPING_ID(land, produkt),
    land,
    produkt;

Die einzelnen Einträge bedeuten:

(land, produkt)

Gruppierung nach Land und Produkt.

(land)

Zwischensumme je Land.

()

Gesamtsumme.

Das Ergebnis enthält keine Gruppierung nur nach Produkt.

LANDPRODUKTUMSATZ
DeutschlandLaptop2.100
DeutschlandMonitor600
ÖsterreichLaptop2.500
ÖsterreichMonitor1.200
DeutschlandNULL2.700
ÖsterreichNULL3.700
NULLNULL6.400

GROUPING SETS ist deshalb sinnvoll, wenn ein Bericht nur bestimmte Aggregationsebenen benötigt.

GROUPING SETS statt mehrerer UNION-ALL-Abfragen

Ohne GROUPING SETS könnte derselbe Bericht mit mehreren Abfragen erstellt werden:

SELECT
    land,
    produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY
    land,
    produkt

UNION ALL

SELECT
    land,
    NULL AS produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY
    land

UNION ALL

SELECT
    NULL AS land,
    NULL AS produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten;

Diese Variante ist länger und fehleranfälliger. Änderungen an Filtern oder Berechnungen müssen in allen Teilabfragen konsistent vorgenommen werden.

Mit GROUPING SETS wird die fachliche Struktur direkt in einer einzigen GROUP BY-Klausel beschrieben.

Partielles ROLLUP verwenden

Nicht immer soll eine Gesamtsumme über sämtliche Daten entstehen.

Mit:

GROUP BY
    land,
    ROLLUP(stadt, produkt)

bleibt land Bestandteil jeder Gruppierung.

Die vollständige Abfrage:

SELECT
    land,
    stadt,
    produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY
    land,
    ROLLUP(stadt, produkt)
ORDER BY
    land,
    GROUPING(stadt),
    stadt,
    GROUPING(produkt),
    produkt;

Sie erzeugt:

(land, stadt, produkt)
(land, stadt)
(land)

Eine Gesamtsumme ohne Land entsteht nicht.

Das ist sinnvoll, wenn beispielsweise für jedes Unternehmen, Mandantengebiet oder Land ein getrennter Bericht erzeugt wird, aber keine gemeinsame Gesamtsumme gewünscht ist.

Der Unterschied lautet:

GROUP BY ROLLUP(land, stadt, produkt)

erzeugt zusätzlich:

()

also eine Gesamtsumme.

Dagegen erzeugt:

GROUP BY land, ROLLUP(stadt, produkt)

keine landübergreifende Gesamtsumme.

WHERE und HAVING richtig einsetzen

WHERE filtert Datensätze vor der Aggregation.

HAVING filtert das bereits aggregierte Ergebnis.

Beispiel für Umsätze aus dem Januar 2026:

SELECT
    land,
    produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
WHERE buchungsdatum >= DATE '2026-01-01'
  AND buchungsdatum <  DATE '2026-02-01'
GROUP BY ROLLUP(land, produkt);

Die Datumsbedingung gehört in die WHERE-Klausel, weil nur bestimmte Ausgangsdatensätze berücksichtigt werden sollen.

Sollen dagegen nur Aggregationen ab einem Umsatz von 1.000 angezeigt werden, wird HAVING verwendet:

SELECT
    land,
    produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
GROUP BY ROLLUP(land, produkt)
HAVING SUM(betrag) >= 1000;

Beide Bedingungen können kombiniert werden:

SELECT
    land,
    produkt,
    SUM(betrag) AS umsatz
FROM umsatz_daten
WHERE buchungsdatum >= DATE '2026-01-01'
  AND buchungsdatum <  DATE '2026-02-01'
GROUP BY ROLLUP(land, produkt)
HAVING SUM(betrag) >= 1000;

Summenzeilen richtig sortieren

Ohne passende ORDER BY-Klausel ist die Reihenfolge der Ergebniszeilen nicht garantiert.

Eine verständliche Sortierung für ROLLUP ist:

ORDER BY
    GROUPING(land),
    land,
    GROUPING(stadt),
    stadt

Dadurch erscheinen:

  1. Detailzeilen des Landes
  2. Zwischensumme des Landes
  3. Gesamtsumme

Alternativ kann GROUPING_ID verwendet werden:

ORDER BY
    GROUPING_ID(land, stadt),
    land,
    stadt

Bei komplexen Berichten kann eine eigene Sortierspalte erzeugt werden:

CASE GROUPING_ID(land, produkt)
    WHEN 0 THEN 1
    WHEN 1 THEN 2
    WHEN 2 THEN 3
    WHEN 3 THEN 4
END AS sortierung

Diese Spalte kann anschließend für die gewünschte Reihenfolge verwendet werden.

Typische Fehler bei ROLLUP, CUBE und GROUPING SETS

Die Reihenfolge innerhalb von ROLLUP ist falsch

ROLLUP(stadt, land)

erzeugt Zwischensummen nach Stadt.

Soll nach Land zusammengefasst werden, ist erforderlich:

ROLLUP(land, stadt)

Künstliche NULL-Werte werden mit echten NULL-Werten verwechselt

Diese Prüfung ist nicht ausreichend:

CASE
    WHEN land IS NULL
    THEN 'Alle Länder'
END

Ein tatsächlich fehlendes Land würde ebenfalls als Gesamtsumme beschriftet.

Richtig ist:

CASE
    WHEN GROUPING(land) = 1
    THEN 'Alle Länder'

    WHEN land IS NULL
    THEN 'Land unbekannt'

    ELSE land
END

Ein vollständiger CUBE erzeugt unnötige Kombinationen

Bei n Gruppierungsspalten kann ein vollständiger CUBE bis zu 2^n Gruppierungskombinationen erzeugen.

SpaltenKombinationen
24
38
416
532
664

Ein CUBE über zahlreiche Spalten kann deshalb sehr große Ergebnismengen erzeugen.

Werden nur ausgewählte Aggregationen benötigt, ist GROUPING SETS meist präziser.

Nicht aggregierte Spalten fehlen in GROUP BY

Diese Abfrage ist ungültig:

SELECT
    land,
    stadt,
    produkt,
    SUM(betrag)
FROM umsatz_daten
GROUP BY ROLLUP(land, stadt);

produkt wird weder aggregiert noch gruppiert.

Oracle meldet in solchen Fällen typischerweise:

ORA-00979: not a GROUP BY expression

Entweder muss produkt in die Gruppierung aufgenommen oder aus der Auswahlliste entfernt werden.

Filter werden an der falschen Stelle angewendet

Eine Bedingung für Ausgangsdatensätze gehört in WHERE.

Eine Bedingung für berechnete Summen gehört in HAVING.

Falsch wäre beispielsweise:

WHERE SUM(betrag) > 1000

Aggregatfunktionen können nicht auf diese Weise in der WHERE-Klausel verwendet werden.

NVL wird vor GROUPING eingesetzt

Eine vorschnelle Ersetzung wie:

NVL(land, 'Alle Länder')

unterscheidet nicht zwischen einem gespeicherten NULL und einer Gesamtsumme.

Zuerst muss GROUPING ausgewertet werden:

CASE
    WHEN GROUPING(land) = 1
    THEN 'Alle Länder'
    ELSE NVL(land, 'Land unbekannt')
END

Performancehinweise

ROLLUP, CUBE und GROUPING SETS sollten fachlich gezielt verwendet werden.

Ausgangsdaten früh einschränken

Selektive Bedingungen gehören in die WHERE-Klausel:

WHERE buchungsdatum >= :von
  AND buchungsdatum <  :bis

Dadurch muss Oracle nur den relevanten Zeitraum aggregieren.

Unnötige CUBE-Dimensionen vermeiden

Ein vollständiger CUBE über viele Spalten erzeugt zahlreiche Gruppierungskombinationen.

Statt:

GROUP BY CUBE(
    jahr,
    monat,
    land,
    stadt,
    produkt,
    vertriebskanal
)

sollten nur fachlich benötigte Gruppierungen mit GROUPING SETS angegeben werden.

Spalten mit sehr hoher Kardinalität prüfen

Eine Gruppierung nach eindeutigen oder nahezu eindeutigen Werten erzeugt kaum Verdichtung.

Beispiele:

Kundennummer
Auftragsnummer
Zeitstempel
Transaktions-ID

Solche Spalten sollten nur aufgenommen werden, wenn die Detailaggregation tatsächlich benötigt wird.

Ausführungsplan kontrollieren

Bei großen Tabellen sollte der Ausführungsplan geprüft werden:

EXPLAIN PLAN FOR
SELECT
    land,
    produkt,
    SUM(betrag)
FROM umsatz_daten
GROUP BY CUBE(land, produkt);

Anschließend:

SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY);

Die tatsächliche Laufzeit hängt unter anderem von Datenmenge, Filtern, Partitionierung, verfügbarer Speichermenge und der Anzahl erzeugter Gruppierungen ab.

Entscheidungshilfe

ROLLUP verwenden, wenn

  • die Daten eine natürliche Hierarchie besitzen,
  • Zwischensummen von der Detailstufe bis zur Gesamtsumme benötigt werden,
  • beispielsweise Jahr, Monat und Tag ausgewertet werden,
  • Land, Region und Stadt hierarchisch zusammengehören.

CUBE verwenden, wenn

  • alle Kombinationen mehrerer Dimensionen benötigt werden,
  • Länder und Produkte unabhängig voneinander ausgewertet werden,
  • neben Detailwerten auch Summen je Dimension benötigt werden,
  • eine mehrdimensionale Analyse erstellt wird.

GROUPING SETS verwenden, wenn

  • nur bestimmte Aggregationsebenen benötigt werden,
  • ein vollständiger CUBE unnötige Ergebnisse erzeugen würde,
  • mehrere UNION ALL-Abfragen ersetzt werden sollen,
  • die Struktur des Berichts ausdrücklich festgelegt werden soll.

GROUPING verwenden, wenn

  • echte NULL-Werte von Summenzeilen unterschieden werden müssen,
  • Zwischensummen verständlich beschriftet werden sollen,
  • einzelne Gruppierungsspalten geprüft werden.

GROUPING_ID verwenden, wenn

  • mehrere Gruppierungsspalten vorhanden sind,
  • Aggregationsebenen gefiltert werden sollen,
  • Detail-, Zwischen- und Gesamtsummen sortiert werden,
  • mehrere GROUPING-Aufrufe vermieden werden sollen.

Häufige Fragen

Was ist der wichtigste Unterschied zwischen ROLLUP und CUBE?

ROLLUP bildet eine Hierarchie von links nach rechts und erzeugt entsprechende Zwischensummen.

CUBE berechnet alle möglichen Kombinationen der angegebenen Dimensionen.

Erzeugt ROLLUP immer eine Gesamtsumme?

Ein vollständiges:

ROLLUP(land, stadt)

erzeugt eine Gesamtsumme.

Bei einem partiellen ROLLUP:

GROUP BY land, ROLLUP(stadt)

bleibt land jedoch in jeder Gruppierung enthalten. Eine landübergreifende Gesamtsumme entsteht dann nicht.

Ist NULL in einer ROLLUP-Abfrage immer eine Summenzeile?

Nein. Die zugrunde liegenden Daten können echte NULL-Werte enthalten.

Nur GROUPING(spalte) = 1 weist sicher auf eine durch die Aggregation erzeugte Summenmarkierung hin.

Kann ich nur die Gesamtsumme ausgeben?

Ja. Bei zwei Gruppierungsspalten kann die Gesamtsumme beispielsweise so gefiltert werden:

HAVING GROUPING_ID(land, produkt) = 3

Kann GROUPING SETS eine UNION-ALL-Abfrage ersetzen?

In vielen Berichten ja. Mehrere Aggregationsebenen können innerhalb einer einzigen GROUP BY-Klausel definiert werden. Dadurch bleibt die Abfrage kompakter und einheitlicher.

Spielt die Reihenfolge bei CUBE eine Rolle?

Die Menge der erzeugten Gruppierungskombinationen bleibt bei denselben Spalten gleich. Die Reihenfolge beeinflusst jedoch die Bitpositionen und damit die Werte von GROUPING_ID.

Bei ROLLUP beeinflusst die Reihenfolge zusätzlich die fachliche Hierarchie der Zwischensummen.

Fazit

ROLLUP, CUBE und GROUPING SETS erweitern normale Oracle-SQL-Aggregationen um Zwischen- und Gesamtsummen.

Die passende Auswahl hängt vom gewünschten Bericht ab:

Hierarchische Zwischensummen:
ROLLUP

Alle Dimensionskombinationen:
CUBE

Gezielt ausgewählte Gruppierungen:
GROUPING SETS

Erkennung künstlicher Summenwerte:
GROUPING

Kennzeichnung und Filterung ganzer Aggregationsebenen:
GROUPING_ID

Für einfache hierarchische Berichte ist ROLLUP meist die passende Lösung. Bei unabhängigen Auswertungsdimensionen kann CUBE sinnvoll sein. Werden nur bestimmte Summenebenen benötigt, verhindert GROUPING SETS unnötige Kombinationen und beschreibt die gewünschte Berichtsstruktur besonders präzise.