Eine Window Function ergänzt eine Spalte, ohne Zeilen zusammenzufassen
GROUP BY fasst viele Zeilen zu einer einzigen zusammen. SQL Window Functions gehen einen anderen Weg: Sie berechnen einen Wert über eine Gruppe zusammengehöriger Zeilen, behalten dabei aber jede einzelne Eingabezeile in der Ausgabe. So bekommst du Detail und Aggregat direkt nebeneinander.
Das Muster ist immer gleich: eine Funktion, gefolgt von OVER (...).
Die Spalte total_all zeigt die Gesamtsumme über alle Zeilen, jeweils in jeder Zeile wiederholt. Die ursprünglichen Zeilen bleiben dabei unangetastet. Zum Vergleich: SELECT SUM(amount) FROM sales liefert zwar dieselbe Zahl, aber eben nur eine einzige Zeile zurück. Mit Window Functions bekommst du beide Sichtweisen auf einmal.
PARTITION BY: Aggregate innerhalb von Gruppen
Ein leeres OVER () aggregiert über die komplette Tabelle. Mit PARTITION BY aggregierst du stattdessen innerhalb von Gruppen – ähnlich wie bei GROUP BY, nur dass die Zeilen wieder erhalten bleiben.
Pro Zeile bekommst du sowohl die Summe der jeweiligen Region als auch den prozentualen Anteil daran. Mit einem reinen GROUP BY wäre die Detailansicht pro Mitarbeiter verloren. Genau das ist die Stärke von Window Functions: Detail und Aggregat in einer einzigen Abfrage.
Ranking in SQLite: ROW_NUMBER, RANK und DENSE_RANK
Die Ranking-Funktionen nummerieren Zeilen anhand der ORDER BY-Klausel innerhalb von OVER. Die drei Varianten unterscheiden sich darin, wie sie mit Gleichständen umgehen.
So liest du die Ausgabe:
ROW_NUMBER()ist immer eindeutig — bei Gleichstand wird die Reihenfolge willkürlich festgelegt. Nimm das, wenn du pro Zeile eine stabile, eindeutige Nummer brauchst.RANK()vergibt bei gleichen Werten denselben Rang und überspringt dann die nächsten Zahlen. Auf zwei Spieler mit Rang 1 folgt direkt Rang 3.DENSE_RANK()vergibt bei Gleichstand ebenfalls denselben Rang, lässt aber keine Lücken. Der nächste Rang ist hier die 2.
Für "Top N pro Gruppe" kombinierst du das Ranking mit PARTITION BY und filterst in einer äußeren Query — denn WHERE kann Window Functions nicht direkt referenzieren:
Top-Verdiener pro Region – die jeweils zwei besten.
LAG und LEAD: Nachbarzeilen einbeziehen
LAG(col) liefert dir den Wert von col aus der vorherigen Zeile innerhalb des Fensters. LEAD(col) greift entsprechend nach vorn auf die nächste Zeile zu. Beide Funktionen sind ideal, wenn du Veränderungen über die Zeit hinweg auswerten willst.
Beim ersten Datensatz ist yesterday gleich NULL – davor gibt es ja schlicht keine Zeile. Du kannst aber einen Standardwert mitgeben: LAG(celsius, 1, celsius) OVER (ORDER BY day) greift auf den heutigen Wert zurück, falls kein Vorgänger existiert.
LEAD funktioniert genau spiegelverkehrt. In Kombination mit PARTITION BY bekommst du Sequenzen pro Gruppe – etwa, wenn du den Umsatz dieses Monats mit dem des Vormonats je Region vergleichen willst.
Laufende Summe in SQLite mit Window Frames
Sobald du innerhalb von OVER ein ORDER BY ergänzt, rechnen Aggregatfunktionen wie SUM, AVG oder COUNT kumulativ:
Zwei Dinge fallen hier auf:
SUM(amount) OVER (ORDER BY day)ergibt eine laufende Summe. Wenn duORDER BYohne expliziten Frame schreibst, ist der Default "vom Anfang des Fensters bis zur aktuellen Zeile".- Die zweite Spalte nutzt einen expliziten Frame:
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW. Das ist ein gleitendes Fenster über 3 Zeilen — also ein gleitender Durchschnitt.
Das mentale Modell für Frames: Jede Window Function rechnet über einen Frame von Zeilen, der relativ zur aktuellen Zeile definiert ist. Die gängigsten Frames:
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW— laufende Summe (der implizite Default).ROWS BETWEEN N PRECEDING AND CURRENT ROW— nachlaufendes Fenster.ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING— die komplette Partition.
ROWS zählt physische Zeilen. Daneben gibt es noch RANGE, das nach Wert gruppiert — praktisch, wenn die ORDER BY-Spalte Gleichstände enthält und du diese als einen einzigen Schritt behandeln willst.
FIRST_VALUE, LAST_VALUE, NTILE
Ein paar weitere Window Functions, die man kennen sollte:
FIRST_VALUEundLAST_VALUEliefern den ersten bzw. letzten Wert innerhalb des Frames. BeiLAST_VALUEsolltest du den Frame im Blick behalten: Standardmäßig endet er beiCURRENT ROW– meistens willst du alsoROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWINGschreiben, um wirklich den letzten Wert der Partition zu bekommen.NTILE(n)teilt die Zeilen innungefähr gleich große Buckets auf – praktisch für Quartile, Perzentile oder A/B-artige Splits.
Fenster mit WINDOW benennen
Wenn mehrere Spalten dieselbe OVER (...)-Klausel verwenden, wird das Wiederholen schnell mühsam. In SQLite kannst du ein Fenster einmal benennen und dann mehrfach wiederverwenden:
Dieselbe Abfrage, weniger Rauschen. Die WINDOW-Klausel steht nach WHERE/GROUP BY/HAVING und vor ORDER BY.
Window Functions vs. GROUP BY
Beide aggregieren – aber sie beantworten unterschiedliche Fragen:
GROUP BYreduziert. Eine Zeile pro Gruppe. Nimm das, wenn dich nur die Zusammenfassung interessiert.- Window Functions erhalten alles. Jede Eingabezeile bleibt bestehen, ergänzt um zusätzliche berechnete Spalten.
Wenn du dich dabei ertappst, ein GROUP BY zu schreiben und die Aggregate anschließend wieder an die Originaltabelle zu joinen, ist das ein deutliches Indiz: Eine Window Function erledigt das in einer einzigen Abfrage.
Ein paar Stolperfallen
WHEREkann keine Window Functions referenzieren. Filter greifen, bevor die Fenster berechnet werden. Pack die Abfrage in eine Subquery oder CTE und filtere auf der äußeren Ebene.- Implizite Frames sind tückisch.
SUM(x) OVER (ORDER BY y)ist eine laufende Summe, weil der Default-FrameRANGE UNBOUNDED PRECEDINGlautet. Willst du die Summe über die gesamte Partition, schreibOVER (PARTITION BY ...)ohneORDER BY– oder gib den Frame explizit an. LAST_VALUEüberrascht jeden beim ersten Mal. Da der Default-Frame an der aktuellen Zeile endet, liefert es den aktuellen Wert zurück, nicht den letzten Wert der Partition. Frame überschreiben!- Window Functions brauchen SQLite 3.25+ (Release 2018). Jede halbwegs aktuelle Installation kann das, aber manche Embedded-Umgebungen hinken hinterher.
Als Nächstes: Generated Columns
Window Functions berechnen zur Abfragezeit. Die nächste Seite zeigt Berechnungen zur Speicherzeit: Generated Columns, bei denen sich der Spaltenwert aus einem Ausdruck ergibt und automatisch aktualisiert wird, sobald sich die zugrundeliegenden Daten ändern.
Häufig gestellte Fragen
Was sind Window Functions in SQLite überhaupt?
Window Functions berechnen einen Wert über eine Gruppe von Zeilen, die mit der aktuellen Zeile zusammenhängen – ohne sie wie bei GROUP BY zusammenzufassen. Du hängst eine OVER (...)-Klausel an Funktionen wie ROW_NUMBER(), RANK(), SUM() oder LAG() an und definierst damit das Fenster. Jede Eingabezeile bleibt im Ergebnis erhalten – du bekommst einfach eine zusätzliche berechnete Spalte.
Worin unterscheiden sich RANK und DENSE_RANK in SQLite?
Beide vergeben einen Rang anhand von ORDER BY, gehen aber unterschiedlich mit Gleichständen um. RANK() lässt nach Gleichständen Lücken: zwei Zeilen auf Rang 1 werden von Rang 3 gefolgt. DENSE_RANK() macht das nicht – die nächste Zeile bekommt Rang 2. Nimm DENSE_RANK(), wenn du fortlaufende Ränge willst, und RANK(), wenn die Lücke eine Aussage hat.
Wie berechne ich eine laufende Summe in SQLite?
Mit SUM(spalte) OVER (ORDER BY ...) plus passendem Window Frame. Sobald du ORDER BY innerhalb von OVER benutzt, gilt standardmäßig der Frame RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW – und genau das ergibt die laufende Summe. Mit zusätzlichem PARTITION BY startet die Summe pro Gruppe wieder bei null.