Du willst wissen, welcher Mitarbeiter den zweitbesten Umsatz in einem Monat erzielt hat. Mit einer klassischen GROUP BY-Abfrage stößt du schnell an Grenzen: Du kannst zwar die Umsätze summieren oder den Durchschnitt berechnen, aber sobald du die Daten gruppierst, gehen die Detailzeilen verloren. Du siehst nur noch aggregierte Werte, nicht mehr die einzelnen Umsätze pro Mitarbeiter oder Tag.
Hier kommen Window Functions ins Spiel. Sie erlauben es dir, Berechnungen über Zeilen hinweg durchzuführen, ohne die einzelnen Datensätze zusammenzufassen. Das bedeutet: Du kannst sowohl die Detailinformationen behalten als auch erweiterte Analysen durchführen – wie Ranglisten, gleitende Durchschnitte oder kumulierte Summen.
In diesem Artikel zeige ich dir:
- Wie du Ranking-Funktionen nutzt, um z. B. den Top-Umsatz pro Verkäufer zu ermitteln.
- Wie du gleitende Durchschnitte berechnest, um Trends in deinen Daten zu erkennen.
- Wie du kumulierte Summen erzeugst, um laufende Gesamtsummen zu verfolgen.
Am Ende wirst du verstehen, warum Window Functions ein echter Game-Changer in SQL sind und wie du sie praktisch einsetzt.
Grundlagen: Die Syntax von Window Functions kurz erklärt
Window Functions sind spezielle SQL-Funktionen, die Berechnungen über ein „Fenster“ von Zeilen durchführen, ohne die einzelnen Zeilen zusammenzufassen. Das bedeutet, du kannst beispielsweise den Durchschnittswert einer Abteilung berechnen, während du die einzelnen Mitarbeiterzeilen behältst.
Das Schlüsselwort OVER()
Jede Window Function nutzt das Schlüsselwort OVER(). Dieses Wandelt eine normale Aggregatfunktion wie SUM() oder AVG() in eine Window Function um. Mit OVER() sagst du SQL, dass die Berechnung über ein bestimmtes Fenster von Zeilen erfolgen soll, aber die Detailzeilen erhalten bleiben.
Die drei Hauptkomponenten innerhalb von OVER()
- PARTITION BY: Unterteilt das Ergebnis in Gruppen, ähnlich wie GROUP BY.
Der Unterschied: Die Zeilen innerhalb jeder Partition bleiben sichtbar. Die Berechnung wird separat für jede Partition durchgeführt. - ORDER BY: Legt die Reihenfolge der Zeilen innerhalb einer Partition fest.
Diese Reihenfolge ist entscheidend für Ranglisten oder kumulative Berechnungen. Ohne ORDER BY wird das Fenster einfach über die gesamte Partition angewendet. - ROWS / RANGE BETWEEN: Definiert den Fensterrahmen – also welche Zeilen relativ zur aktuellen Zeile in die Berechnung einfließen.
Zum Beispiel: „die 3 vorherigen Zeilen bis zur aktuellen Zeile“ oder „von Beginn der Partition bis zur aktuellen Zeile“.
Dies ist der fortgeschrittene, aber mächtigste Teil von Window Functions.
Ein einfaches Beispiel
Du möchtest den Durchschnittsgehalt pro Abteilung berechnen, aber alle Mitarbeiterzeilen sollen erhalten bleiben:
SELECT
mitarbeiter,
abteilung,
gehalt,
AVG(gehalt) OVER (PARTITION BY abteilung) AS avg_gehalt_abteilung
FROM gehaltsdaten;In diesem Beispiel:
- PARTITION BY abteilung sorgt dafür, dass der Durchschnitt für jede Abteilung separat berechnet wird.
- Jede Mitarbeiterzeile bleibt erhalten, du verlierst keine Details.
So kannst du zum Beispiel sehen, wie jeder Mitarbeiter im Vergleich zum Durchschnitt seiner Abteilung abschneidet.
Anwendungsfall 1: Ranking-Funktionen (ROW_NUMBER, RANK, DENSE_RANK)
Ranking-Funktionen in SQL helfen dir, Zeilen innerhalb einer Partition eine Rangfolge zuzuweisen. Typische Anwendungsfälle sind: die besten Verkäufer pro Monat finden, Top-Produkte nach Umsatz ermitteln oder Platzierungen innerhalb eines Teams berechnen.
Die wichtigsten Ranking-Funktionen
- ROW_NUMBER(): Vergibt eine eindeutige, fortlaufende Nummer an jede Zeile, auch bei gleichen Werten. Beispiel: 1, 2, 3, 4…
- RANK(): Vergibt Ränge und lässt Lücken, wenn Werte gleich sind. Beispiel: 1, 2, 2, 4…
- DENSE_RANK(): Vergibt Ränge ohne Lücken bei gleichen Werten. Beispiel: 1, 2, 2, 3…
Praxis-Beispiel: Top 3-Umsatz pro Verkäufer
Angenommen, du möchtest herausfinden, welche Umsätze jeder Verkäufer in seinem Team erzielt hat und nur die Top 3 pro Verkäufer anzeigen:
SELECT
verkaeufer,
datum,
umsatz,
RANK() OVER (PARTITION BY verkaeufer ORDER BY umsatz DESC) AS rang
FROM verkaufszahlen
QUALIFY RANK() <= 3;
Erklärung:
- PARTITION BY verkaeufer sorgt dafür, dass der Rang für jeden Verkäufer separat berechnet wird.
- ORDER BY umsatz DESC sortiert die Umsätze innerhalb der Partition vom höchsten zum niedrigsten.
- QUALIFY RANK() <= 3 filtert die Zeilen auf die Top 3 pro Verkäufer. Hinweis: QUALIFY wird in SQL-Dialekten wie BigQuery oder Snowflake unterstützt.
Mit diesen Funktionen kannst du also sehr flexibel Ranglisten erstellen und unterschiedliche Szenarien abbilden – von eindeutigen Platzierungen bis hin zu Rängen mit Gleichständen.
Anwendungsfall 2: Gleitende Durchschnitte (Moving Average)
Gleitende Durchschnitte helfen, Trends in deinen Daten zu erkennen und Schwankungen zu glätten. Typische Beispiele sind tägliche Besucherzahlen einer Website, Lagerbestände oder Umsätze über die Zeit.
Fokus auf ROWS BETWEEN
Hier kommt der Fensterrahmen voll zum Einsatz. Mit ROWS BETWEEN kannst du genau festlegen, welche Zeilen relativ zur aktuellen Zeile in die Berechnung einfließen. Zum Beispiel: „die letzten 6 Tage plus heute“ oder „von Beginn der Zeitreihe bis heute“.
Praxis-Beispiel: 7-Tage gleitender Durchschnitt der Website-Besucher
SELECT
datum,
besucher,
AVG(besucher) OVER (
ORDER BY datum
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7days
FROM besucherzahlen
ORDER BY datum;
Erklärung:
- ORDER BY datum sortiert die Daten chronologisch.
- ROWS BETWEEN 6 PRECEDING AND CURRENT ROW definiert das Fenster: Die letzten 6 Tage plus der aktuelle Tag = 7 Tage.
- AVG(besucher) OVER (…) berechnet den Durchschnitt innerhalb dieses Fensters.
Variationen
- BETWEEN 3 PRECEDING AND 1 FOLLOWING: Ein symmetrisches Fenster – 3 Zeilen davor und 1 Zeile danach.
- BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: Kumulativ von Beginn der Datenreihe bis zur aktuellen Zeile.
Gleitende Durchschnitte sind ein mächtiges Werkzeug für die Trendanalyse und zeigen, wie sich Werte über die Zeit verändern, ohne einzelne Datenpunkte zu verlieren.
Anwendungsfall 3: Kumulierte Summen (Cumulative Sum - CumSum)
Kumulierte Summen zeigen den laufenden Gesamtwert bis zur aktuellen Zeile. Das ist besonders nützlich, um beispielsweise den Gesamtumsatz eines Produkts über die Zeit oder Lagerbestände nachzuvollziehen.
Praxis-Beispiel: Kumulierten Umsatz pro Produkt berechnen
SELECT
produkt,
datum,
taeglicher_umsatz,
SUM(taeglicher_umsatz) OVER (
PARTITION BY produkt
ORDER BY datum
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS kumulierter_umsatz
FROM umsatzdaten
ORDER BY produkt, datum;
Erklärung:
- PARTITION BY produkt: Berechnet die kumulierte Summe separat für jedes Produkt.
- ORDER BY datum: Sortiert die Zeilen chronologisch, damit die kumulierte Summe die Reihenfolge der Zeit widerspiegelt.
- ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: Der Fensterrahmen startet ganz am Anfang der Partition und endet bei der aktuellen Zeile. So wird die Summe von Beginn bis jetzt berechnet.
- SUM(taeglicher_umsatz) OVER (…): Berechnet die laufende Gesamtsumme innerhalb des definierten Fensters.
Mit kumulierten Summen behältst du jederzeit den Überblick über die Gesamtsummen in deinen Daten, ohne die Detailzeilen zu verlieren.
Kombinierte Anwendung: Ein komplexeres Praxisbeispiel
In der Praxis möchtest du oft mehrere Window Functions gleichzeitig nutzen. Zum Beispiel, um die monatliche Leistung der Mitarbeiter zu analysieren, ihren Umsatz mit dem Team-Durchschnitt zu vergleichen, ihre Rangfolge zu bestimmen und den kumulierten Team-Umsatz zu berechnen.
Praxis-Beispiel: Monatsleistung der Mitarbeiter analysieren
SELECT
mitarbeiter,
team,
monat,
umsatz,
-- Durchschnitt pro Team und Monat
AVG(umsatz) OVER (PARTITION BY team, monat) AS avg_team_umsatz,
-- Rang im Team pro Monat
RANK() OVER (PARTITION BY team, monat ORDER BY umsatz DESC) AS rang_im_team,
-- Laufende Summe des Teams im Monat (Reihenfolge nach Umsatz)
SUM(umsatz) OVER (
PARTITION BY team, monat
ORDER BY umsatz DESC
ROWS UNBOUNDED PRECEDING
) AS kum_sum_team_monat
FROM leistungsdaten
ORDER BY team, monat, rang_im_team;
Erklärung:
- AVG(umsatz) OVER (PARTITION BY team, monat): Berechnet den Durchschnittsumsatz pro Team und Monat, ohne die einzelnen Zeilen zu aggregieren.
- RANK() OVER (PARTITION BY team, monat ORDER BY umsatz DESC): Vergibt einen Rang im Team basierend auf dem Umsatz, wobei gleiche Umsätze gleiche Ränge erhalten.
- SUM(umsatz) OVER (PARTITION BY team, monat ORDER BY umsatz DESC ROWS UNBOUNDED PRECEDING): Berechnet den kumulierten Umsatz innerhalb des Teams für den Monat in der Reihenfolge der Umsätze.
Mit dieser kombinierten Anwendung siehst du auf einen Blick:
- Wie jeder Mitarbeiter im Vergleich zu seinem Team abschneidet
- Wer die Top-Umsätze erzielt
- Wie sich der Gesamtumsatz innerhalb des Teams entwickelt
Performance-Hinweise und Best Practices
Window Functions sind mächtig, können aber bei großen Datenmengen die Performance beeinträchtigen. Mit ein paar einfachen Regeln kannst du deine Abfragen effizienter gestalten.
Indexierung
Ein Index auf den Spalten, die in PARTITION BY und ORDER BY verwendet werden, kann die Berechnung erheblich beschleunigen. Dadurch muss die Datenbank die Zeilen nicht jedes Mal neu sortieren.
Fenstergröße im Blick behalten
Große Fensterrahmen wie BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING sind sehr rechenintensiv. Überlege, ob du wirklich alle Zeilen benötigst oder ein kleineres, begrenztes Fenster ausreicht.
Über die Partition nachdenken
Die Wahl der Partition beeinflusst die Performance:
- Zu viele kleine Partitionen: Die Berechnung wird oft wiederholt, was ineffizient sein kann.
- Zu wenige große Partitionen: Das Fenster kann sehr groß werden und die Berechnung verlangsamen.
Weitere Tipps
- Vermeide komplexe Berechnungen innerhalb des Fensters, wenn möglich.
- Teste deine Abfragen mit kleineren Datenmengen, bevor du sie auf große Tabellen anwendest.
- Nutze Datenbank-spezifische Optimierungen, z.B. QUALIFY in BigQuery oder Snowflake, um Ergebnisse nach Window Functions zu filtern.
Mit diesen Best Practices stellst du sicher, dass Window Functions nicht nur mächtig, sondern auch effizient bleiben.
Fazit: Praktische Methode
Window Functions erweitern deine SQL-Fähigkeiten enorm und lösen viele Probleme, die mit klassischen GROUP BY-Abfragen nur umständlich zu lösen sind. Du kannst Berechnungen über Zeilen hinweg durchführen, ohne die Detailzeilen zu verlieren.
Key Takeaway
Der magische Dreiklang, den du dir merken solltest, ist:
- PARTITION BY: Gruppieren, aber die Zeilen bleiben erhalten.
- ORDER BY: Sortieren der Zeilen innerhalb der Partition, entscheidend für Rangfolgen und kumulative Berechnungen.
- ROWS BETWEEN: Den Fensterrahmen setzen, also welche Zeilen in die Berechnung einbezogen werden.
Praxis-Tipp
Probiere die Beispiele in deiner eigenen Datenbank aus. Experimentiere mit den Einstellungen von Partitionen, Fenstergrößen und Sortierreihenfolgen. Überlege, welche Analysen für deine eigenen Daten spannend sind – vielleicht findest du neue Insights, die vorher verborgen waren.
Was ist eigentlich NoSQL – und was hat das mit SQL zu tun?
NoSQL ist in den letzten Jahren zu einem zentralen Begriff...
Artikel lesenSQL-Performance-Tipps: Wie du deine Abfragen schneller machst
Die Performance von SQL-Queries ist ein entscheidender Faktor, der sowohl...
Artikel lesenSortieren mit ORDER BY: ASC und DESC verstehen
Daten aus einer Datenbank liegen oft in unsortierter Form vor....
Artikel lesen5 SQL-Funktionen, die kaum jemand richtig kennt
SQL ist aus der Arbeit mit Datenbanken nicht wegzudenken. Jeder,...
Artikel lesen