Wenn du Window Functions benutzt, kommt die Frage auf: "Wie viele Zeilen im Fenster werden für die Berechnung des Werts der aktuellen Zeile verwendet?" Die Antwort darauf hängt vom Fenster-Frame ab.
Fenster-Frame ist der Bereich von Zeilen, der für das Ergebnis der Window Function genutzt wird. Dieser Bereich wird auf Basis der aktuellen Zeile und zusätzlicher Bedingungen gebaut, die du mit ROWS oder RANGE angibst.
Ein einfaches Beispiel: Wenn du eine kumulierte Summe berechnest, kannst du angeben:
- Nur die aktuelle Zeile berücksichtigen.
- Die aktuelle Zeile und alle Zeilen darüber berücksichtigen.
- Die aktuelle Zeile und eine feste Anzahl Zeilen darüber/darunter berücksichtigen.
Genau ROWS und RANGE steuern, welche Zeilen in den Fenster-Frame kommen.
Verwendung von ROWS
ROWS definiert den Fenster-Frame auf Basis der physischen Reihenfolge der Zeilen. Das heißt, es zählt die Zeilen von oben nach unten in ihrer Reihenfolge, egal welche Werte in diesen Zeilen stehen.
Syntax
fenster_funktion OVER (
ORDER BY spalte
ROWS BETWEEN start AND ende
)
Wichtige Ausdrücke:
CURRENT ROW— aktuelle Zeile.zahl PRECEDING— bestimmte Anzahl Zeilen über der aktuellen.zahl FOLLOWING— bestimmte Anzahl Zeilen unter der aktuellen.UNBOUNDED PRECEDING— vom Anfang des Fensters.UNBOUNDED FOLLOWING— bis zum Ende des Fensters.
Beispiel: Kumulative Summe für aktuelle und 2 vorherige Zeilen
SELECT
employee_id,
salary,
SUM(salary) OVER (
ORDER BY employee_id
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS rolling_sum
FROM employees;
Erklärung:
-
ROWS BETWEEN 2 PRECEDING AND CURRENT ROWbedeutet: Nimm die aktuelle Zeile und zwei Zeilen darüber. - Die kumulierte Summe wird nur für diese drei Zeilen berechnet.
Ergebnis:
| employee_id | salary | rolling_sum |
|---|---|---|
| 1 | 5000 | 5000 |
| 2 | 7000 | 12000 |
| 3 | 6000 | 18000 |
| 4 | 4000 | 17000 |
Beispiel: "Rolling Window"-Analyse mit fester Zeilenzahl
Aufgabe: Berechne das Durchschnittsgehalt für die aktuelle Zeile und die zwei folgenden.
SELECT
employee_id,
salary,
AVG(salary) OVER (
ORDER BY employee_id
ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING
) AS rolling_avg
FROM employees;
Ergebnis:
| employee_id | salary | rolling_avg |
|---|---|---|
| 1 | 5000 | 6000 |
| 2 | 7000 | 5666.67 |
| 3 | 6000 | 5000 |
| 4 | 4000 | 4000 |
Verwendung von RANGE
RANGE baut den Fenster-Frame auf Basis von Werten, nicht auf der Position der Zeilen. Das heißt, Zeilen werden in den Frame aufgenommen, wenn ihre Werte in der ORDER BY-Spalte im angegebenen Bereich liegen.
Syntax
fenster_funktion OVER (
ORDER BY spalte
RANGE BETWEEN start AND ende
)
Beispiel: Kumulative Summe nach Wertebereich
Aufgabe: Berechne die kumulierte Summe für Zeilen, bei denen das Gehalt sich von der aktuellen Zeile um nicht mehr als 2000 unterscheidet.
SELECT
employee_id,
salary,
SUM(salary) OVER (
ORDER BY salary
RANGE BETWEEN 2000 PRECEDING AND 2000 FOLLOWING
) AS range_sum
FROM employees;
Erklärung:
RANGE BETWEEN 2000 PRECEDING AND 2000 FOLLOWINGbedeutet: Nimm die Zeilen, bei denen der Wert vonsalaryim Bereich ±2000 von der aktuellen Zeile liegt.
Ergebnis:
| employee_id | salary | range_sum |
|---|---|---|
| 4 | 4000 | 10000 |
| 3 | 6000 | 17000 |
| 2 | 7000 | 17000 |
| 1 | 5000 | 17000 |
Vergleich von ROWS und RANGE
ROWSarbeitet mit echten Zeilen und deren Anzahl. Es hängt nicht von den Werten ab.RANGEarbeitet mit einem logischen Wertebereich, der für die Spalte ausORDER BYangegeben wird.
Zum Vergleich ein Beispiel. Angenommen, wir haben eine Tabelle sales mit Daten:
| id | amount |
|---|---|
| 1 | 100 |
| 2 | 100 |
| 3 | 300 |
| 4 | 400 |
Vergleichen wir die Abfragen:
ROWS:
SELECT
id,
SUM(amount) OVER (
ORDER BY amount
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS sum_rows
FROM sales;
Ergebnis:
| id | sum_rows |
|---|---|
| 1 | 100 |
| 2 | 200 |
| 3 | 500 |
| 4 | 900 |
Hier wird jede Zeile vom System zur Summe hinzugefügt, sobald sie wirklich erscheint.
RANGE:
SELECT
id,
SUM(amount) OVER (
ORDER BY amount
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS sum_range
FROM sales;
Ergebnis:
| id | sum_range |
|---|---|
| 1 | 200 |
| 2 | 200 |
| 3 | 500 |
| 4 | 900 |
Hier werden die Zeilen 1 und 2 zusammengefasst, weil ihr amount = 100 ist. RANGE berücksichtigt wiederholte Werte in der Spalte amount.
Beispiele für echte Aufgaben
- Berechnung des Umsatzwachstums
Aufgabe: Berechne die Veränderung des Umsatzes im Vergleich zur vorherigen Zeile.
SELECT
month,
revenue,
revenue - LAG(revenue) OVER (
ORDER BY month
) AS revenue_change
FROM sales_data;
- Vergleich der aktuellen Zeile mit dem Durchschnitt in der Gruppe
Aufgabe: Für jede Abteilung die Differenz zwischen dem Gehalt des Mitarbeiters und dem Durchschnittsgehalt der Abteilung berechnen.
SELECT
department_id,
employee_id,
salary,
salary - AVG(salary) OVER (
PARTITION BY department_id
) AS salary_diff
FROM employees;
Fehler bei der Verwendung von ROWS und RANGE
Falsch angegebene Zeilenreihenfolge (ORDER BY): Wenn du keine Sortierreihenfolge angibst, gibt PostgreSQL einen Fehler aus, weil es die aktuelle Zeile nicht bestimmen kann.
Vermischung von ROWS und RANGE in einer Aufgabe: Wähle den Ansatz je nach deinen Daten. ROWS ist für Aufgaben mit fester Zeilenzahl geeignet, RANGE — für Wertebereiche.
Überspringen von wiederholten Werten bei RANGE: Denk daran, dass RANGE alle wiederholten Werte berücksichtigt, was das Ergebnis stark verändern kann.
GO TO FULL VERSION