Fensterfunktionen
DuckDB unterstützt Fensterfunktionen, die mehrere Zeilen verwenden können, um für jede Zeile einen Wert zu berechnen. Fensterfunktionen sind blockierende Operatoren, d. h. sie müssen ihre gesamte Eingabe puffern und gehören damit zu den speicherintensivsten Operatoren in SQL.
Fensterfunktionen gibt es in SQL seit SQL:2003 und sie werden von den großen SQL-Datenbanksystemen unterstützt.
Beispiele
Erzeugt eine Spalte row_number, um Zeilen zu nummerieren:
SELECT row_number() OVER ()FROM sales;Tipp Wenn Sie nur eine Nummer für jede Zeile in einer Tabelle brauchen, können Sie die Pseudospalte
rowidverwenden.
Erzeugt eine Spalte row_number, um Zeilen nach time geordnet zu nummerieren:
SELECT row_number() OVER (ORDER BY time)FROM sales;Erzeugt eine Spalte row_number, um Zeilen nach time geordnet und nach region partitioniert zu nummerieren:
SELECT row_number() OVER (PARTITION BY region ORDER BY time)FROM sales;Berechnet die Differenz zwischen dem aktuellen und dem vorherigen (nach time) amount:
SELECT amount - lag(amount) OVER (ORDER BY time)FROM sales;Berechnet den Anteil am gesamten amount der Verkäufe pro region für jede Zeile:
SELECT amount / sum(amount) OVER (PARTITION BY region)FROM sales;Syntax
Fensterfunktionen können nur in der SELECT-Klausel verwendet werden. Um OVER-Spezifikationen zwischen Funktionen zu teilen, verwenden Sie die WINDOW-Klausel der Anweisung und die Syntax OVER ⟨window_name⟩{:.language-sql .highlight}.
Allgemeine Fensterfunktionen
Die folgende Tabelle zeigt die verfügbaren allgemeinen Fensterfunktionen.
| Name | Beschreibung |
|---|---|
cume_dist([ORDER BY ordering]) |
Die kumulative Verteilung: (Anzahl der Partitionszeilen vor oder gleichrangig mit der aktuellen Zeile) / Gesamtzahl der Partitionszeilen. |
dense_rank() |
Der Rang der aktuellen Zeile ohne Lücken; diese Funktion zählt Peer-Gruppen. |
fill(expr [ ORDER BY ordering]) |
Füllt fehlende Werte per linearer Interpolation, mit ORDER BY als X-Achse. |
first_value(expr[ ORDER BY ordering][ IGNORE NULLS]) |
Gibt expr ausgewertet an der Zeile zurück, die die erste Zeile (mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) des Fensterrahmens ist. |
lag(expr[, offset[, default]][ ORDER BY ordering][ IGNORE NULLS]) |
Gibt expr ausgewertet an der Zeile zurück, die offset Zeilen (unter Zeilen mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) vor der aktuellen Zeile im Fensterrahmen liegt; gibt es keine solche Zeile, wird stattdessen default zurückgegeben (muss denselben Typ wie expr haben). Sowohl offset als auch default werden bezüglich der aktuellen Zeile ausgewertet. Wird offset weggelassen, ist der Standard 1 und default NULL. |
last_value(expr[ ORDER BY ordering][ IGNORE NULLS]) |
Gibt expr ausgewertet an der Zeile zurück, die die letzte Zeile (unter Zeilen mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) des Fensterrahmens ist. |
lead(expr[, offset[, default]][ ORDER BY ordering][ IGNORE NULLS]) |
Gibt expr ausgewertet an der Zeile zurück, die offset Zeilen nach der aktuellen Zeile (unter Zeilen mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) im Fensterrahmen liegt; gibt es keine solche Zeile, wird stattdessen default zurückgegeben (muss denselben Typ wie expr haben). Sowohl offset als auch default werden bezüglich der aktuellen Zeile ausgewertet. Wird offset weggelassen, ist der Standard 1 und default NULL. |
nth_value(expr, nth[ ORDER BY ordering][ IGNORE NULLS]) |
Gibt expr ausgewertet an der n-ten Zeile (unter Zeilen mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) des Fensterrahmens zurück (Zählung ab 1); NULL, wenn es keine solche Zeile gibt. |
ntile(num_buckets[ ORDER BY ordering]) |
Eine ganze Zahl von 1 bis num_buckets, die die Partition möglichst gleichmäßig teilt. |
percent_rank([ORDER BY ordering]) |
Der relative Rang der aktuellen Zeile: (rank() - 1) / (total partition rows - 1). |
rank([ORDER BY ordering]) |
Der Rang der aktuellen Zeile mit Lücken; gleich dem row_number ihres ersten Peers. |
row_number([ORDER BY ordering]) |
Die Nummer der aktuellen Zeile innerhalb der Partition, Zählung ab 1. |
cume_dist([ORDER BY ordering])
| Beschreibung | Die kumulative Verteilung: (Anzahl der Partitionszeilen vor oder gleichrangig mit der aktuellen Zeile) / Gesamtzahl der Partitionszeilen. Ist eine ORDER BY-Klausel angegeben, wird die Verteilung innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung berechnet. |
| Rückgabetyp | DOUBLE |
| Beispiel | cume_dist() |
dense_rank()
| Beschreibung | Der Rang der aktuellen Zeile ohne Lücken; diese Funktion zählt Peer-Gruppen. |
| Rückgabetyp | BIGINT |
| Beispiel | dense_rank() |
| Aliase | rank_dense() |
fill(expr[ ORDER BY ordering])
| Beschreibung | Ersetzt NULL-Werte von expr durch eine lineare Interpolation auf Basis der nächsten Nicht-NULL-Werte und der Sortierwerte. Beide Werte müssen Arithmetik unterstützen, und es darf nur einen Ordnungsschlüssel geben. Für fehlende Werte an den Enden wird lineare Extrapolation verwendet. Scheitert die Interpolation, bleibt der NULL-Wert erhalten. |
| Rückgabetyp | Derselbe Typ wie expr |
| Beispiel | fill(column) |
first_value(expr[ ORDER BY ordering][ IGNORE NULLS])
| Beschreibung | Gibt expr ausgewertet an der Zeile zurück, die die erste Zeile (mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) des Fensterrahmens ist. Ist eine ORDER BY-Klausel angegeben, wird die erste Zeilennummer innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung berechnet. |
| Rückgabetyp | Derselbe Typ wie expr |
| Beispiel | first_value(column) |
lag(expr[, offset[, default]][ ORDER BY ordering][ IGNORE NULLS])
| Beschreibung | Gibt expr ausgewertet an der Zeile zurück, die offset Zeilen (unter Zeilen mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) vor der aktuellen Zeile im Fensterrahmen liegt; gibt es keine solche Zeile, wird stattdessen default zurückgegeben (muss denselben Typ wie expr haben). Sowohl offset als auch default werden bezüglich der aktuellen Zeile ausgewertet. Wird offset weggelassen, ist der Standard 1 und default NULL. Ist eine ORDER BY-Klausel angegeben, wird die verzögerte Zeilennummer innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung berechnet. |
| Rückgabetyp | Derselbe Typ wie expr |
| Beispiel | lag(column, 3, 0) |
last_value(expr[ ORDER BY ordering][ IGNORE NULLS])
| Beschreibung | Gibt expr ausgewertet an der Zeile zurück, die die letzte Zeile (unter Zeilen mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) des Fensterrahmens ist. Wird offset weggelassen, ist der Standard 1 und default NULL. Ist eine ORDER BY-Klausel angegeben, wird die letzte Zeile innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung bestimmt. |
| Rückgabetyp | Derselbe Typ wie expr |
| Beispiel | last_value(column) |
lead(expr[, offset[, default]][ ORDER BY ordering][ IGNORE NULLS])
| Beschreibung | Gibt expr ausgewertet an der Zeile zurück, die offset Zeilen nach der aktuellen Zeile (unter Zeilen mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) im Fensterrahmen liegt; gibt es keine solche Zeile, wird stattdessen default zurückgegeben (muss denselben Typ wie expr haben). Sowohl offset als auch default werden bezüglich der aktuellen Zeile ausgewertet. Wird offset weggelassen, ist der Standard 1 und default NULL. Ist eine ORDER BY-Klausel angegeben, wird die vorausliegende Zeilennummer innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung berechnet. |
| Rückgabetyp | Derselbe Typ wie expr |
| Beispiel | lead(column, 3, 0) |
nth_value(expr, nth[ ORDER BY ordering][ IGNORE NULLS])
| Beschreibung | Gibt expr ausgewertet an der n-ten Zeile (unter Zeilen mit einem Nicht-Null-Wert von expr, wenn IGNORE NULLS gesetzt ist) des Fensterrahmens zurück (Zählung ab 1); NULL, wenn es keine solche Zeile gibt. Ist eine ORDER BY-Klausel angegeben, wird die n-te Zeilennummer innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung berechnet. |
| Rückgabetyp | Derselbe Typ wie expr |
| Beispiel | nth_value(column, 2) |
ntile(num_buckets[ ORDER BY ordering])
| Beschreibung | Eine ganze Zahl von 1 bis num_buckets, die die Partition möglichst gleichmäßig teilt. Ist eine ORDER BY-Klausel angegeben, wird das ntile innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung berechnet. |
| Rückgabetyp | BIGINT |
| Beispiel | ntile(4) |
percent_rank([ORDER BY ordering])
| Beschreibung | Der relative Rang der aktuellen Zeile: (rank() - 1) / (total partition rows - 1). Ist eine ORDER BY-Klausel angegeben, wird der relative Rang innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung berechnet. |
| Rückgabetyp | DOUBLE |
| Beispiel | percent_rank() |
rank([ORDER BY ordering])
| Beschreibung | Der Rang der aktuellen Zeile mit Lücken; gleich dem row_number ihres ersten Peers. Ist eine ORDER BY-Klausel angegeben, wird der Rang innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung berechnet. |
| Rückgabetyp | BIGINT |
| Beispiel | rank() |
row_number([ORDER BY ordering])
| Beschreibung | Die Nummer der aktuellen Zeile innerhalb der Partition, Zählung ab 1. Ist eine ORDER BY-Klausel angegeben, wird die Zeilennummer innerhalb des Rahmens mit der angegebenen Ordnung statt der Rahmenordnung berechnet. |
| Rückgabetyp | BIGINT |
| Beispiel | row_number() |
Aggregat-Fensterfunktionen
Alle Aggregatfunktionen können in einem Fensterkontext verwendet werden, einschließlich der optionalen FILTER-Klausel.
Die Aggregatfunktionen first und last werden von den jeweiligen allgemeinen Fensterfunktionen überdeckt, mit der kleinen Folge, dass die FILTER-Klausel für diese nicht verfügbar ist, IGNORE NULLS jedoch schon.
DISTINCT-Argumente
Alle Aggregat-Fensterfunktionen unterstützen eine DISTINCT-Klausel für die Argumente. Ist die DISTINCT-Klausel
angegeben, werden nur eindeutige Werte in die Berechnung des Aggregats einbezogen. Das wird typischerweise zusammen
mit dem Aggregat COUNT verwendet, um die Anzahl eindeutiger Elemente zu erhalten; es kann aber mit jeder Aggregatfunktion
im System verwendet werden. Es gibt Aggregate, die unempfindlich gegenüber Duplikaten sind (z. B. min, max); für
diese wird die Klausel geparst und ignoriert.
-- Count the number of distinct users at a given point in timeSELECT count(DISTINCT name) OVER (ORDER BY time) FROM sales;-- Concatenate those distinct users into a listSELECT list(DISTINCT name) OVER (ORDER BY time) FROM sales;ORDER BY-Argumente
Alle Aggregat-Fensterfunktionen unterstützen eine ORDER BY-Argumentklausel, die sich von der Fensterordnung unterscheidet.
Ist die ORDER BY-Argumentklausel angegeben, werden die zu aggregierenden Werte vor Anwendung der Funktion sortiert.
Normalerweise ist das unwichtig, aber es gibt ordnungssensitive Aggregate, die unbestimmte Ergebnisse haben können (z. B.
mode, list und string_agg). Diese können durch das Ordnen der Argumente deterministisch gemacht werden. Für ordnungsunempfindliche
Aggregate wird diese Klausel geparst und ignoriert.
-- Compute the modal value up to each time, breaking ties in favor of the most recent value.SELECT mode(value ORDER BY time DESC) OVER (ORDER BY time) FROM sales;Der SQL-Standard sieht ORDER BY bei allgemeinen Fensterfunktionen nicht vor, wir haben jedoch alle
dieser Funktionen (außer dense_rank) so erweitert, dass sie diese Syntax akzeptieren und Framing verwenden, um den Bereich einzuschränken, auf den die sekundäre
Ordnung angewendet wird.
-- Compare each athlete's time in an event with the best time to dateSELECT event, date, athlete, time, first_value(time ORDER BY time ASC) OVER w AS record_time, first_value(athlete ORDER BY time ASC) OVER w AS record_athlete,FROM meet_resultsWINDOW w AS (PARTITION BY event ORDER BY datetime)ORDER BY ALL;Beachten Sie, dass zwischen den Argumenten und der ORDER BY-Klausel kein Komma steht.
Nulls
Alle allgemeinen Fensterfunktionen, die IGNORE NULLS akzeptieren, respektieren Nulls standardmäßig. Dieses Standardverhalten kann optional mit RESPECT NULLS explizit gemacht werden.
Im Gegensatz dazu ignorieren alle Aggregat-Fensterfunktionen (außer list und seinen Aliasen, die Nulls über ein FILTER ignorieren können) Nulls und akzeptieren kein RESPECT NULLS. Zum Beispiel berechnet sum(column) OVER (ORDER BY time) AS cumulativeColumn eine kumulierte Summe, bei der Zeilen mit einem NULL-Wert von column denselben Wert von cumulativeColumn haben wie die Zeile davor.
Auswertung
Das Fenstern zerlegt eine Relation in unabhängige Partitionen,
ordnet diese Partitionen
und berechnet dann für jede Zeile eine neue Spalte als Funktion der benachbarten Werte.
Einige Fensterfunktionen hängen nur von der Partitionsgrenze und der Ordnung ab,
einige wenige (einschließlich aller Aggregate) verwenden außerdem einen Rahmen.
Rahmen werden als Anzahl von Zeilen auf beiden Seiten (preceding oder following) der aktuellen Zeile angegeben.
Die Distanz kann als Anzahl von Zeilen (ROWS),
als Wertebereich (RANGE) anhand des Ordnungswerts der Partition und einer Distanz
oder als Anzahl von Gruppen (Mengen von Zeilen mit demselben Sortierwert) angegeben werden.
Die vollständige Syntax ist im Diagramm oben auf der Seite gezeigt, und dieses Diagramm veranschaulicht die Auswertungsumgebung:
Partition und Ordnung
Das Partitionieren zerlegt die Relation in unabhängige, voneinander unabhängige Teile. Das Partitionieren ist optional; ist keines angegeben, wird die gesamte Relation als eine einzige Partition behandelt. Fensterfunktionen können nicht auf Werte außerhalb der Partition zugreifen, die die Zeile enthält, an der sie ausgewertet werden.
Die Ordnung ist ebenfalls optional, aber ohne sie sind die Ergebnisse allgemeiner Fensterfunktionen und ordnungssensitiver Aggregatfunktionen sowie die Reihenfolge des Framings nicht wohldefiniert. Jede Partition wird mit derselben Ordnungsklausel geordnet.
Hier ist eine Tabelle mit Stromerzeugungsdaten, verfügbar als CSV-Datei (power-plant-generation-history.csv). Zum Laden der Daten führen Sie aus:
CREATE TABLE "Generation History" AS FROM 'power-plant-generation-history.csv';Nach dem Partitionieren nach Kraftwerk und dem Ordnen nach Datum hat sie dieses Layout:
| Plant | Date | MWh |
|---|---|---|
| Boston | 2019-01-02 | 564337 |
| Boston | 2019-01-03 | 507405 |
| Boston | 2019-01-04 | 528523 |
| Boston | 2019-01-05 | 469538 |
| Boston | 2019-01-06 | 474163 |
| Boston | 2019-01-07 | 507213 |
| Boston | 2019-01-08 | 613040 |
| Boston | 2019-01-09 | 582588 |
| Boston | 2019-01-10 | 499506 |
| Boston | 2019-01-11 | 482014 |
| Boston | 2019-01-12 | 486134 |
| Boston | 2019-01-13 | 531518 |
| Worcester | 2019-01-02 | 118860 |
| Worcester | 2019-01-03 | 101977 |
| Worcester | 2019-01-04 | 106054 |
| Worcester | 2019-01-05 | 92182 |
| Worcester | 2019-01-06 | 94492 |
| Worcester | 2019-01-07 | 99932 |
| Worcester | 2019-01-08 | 118854 |
| Worcester | 2019-01-09 | 113506 |
| Worcester | 2019-01-10 | 96644 |
| Worcester | 2019-01-11 | 93806 |
| Worcester | 2019-01-12 | 98963 |
| Worcester | 2019-01-13 | 107170 |
Im Folgenden verwenden wir diese Tabelle (oder kleine Ausschnitte davon), um verschiedene Teile der Auswertung von Fensterfunktionen zu veranschaulichen.
Die einfachste Fensterfunktion ist row_number().
Diese Funktion berechnet nur die 1-basierte Zeilennummer innerhalb der Partition mit der Abfrage:
SELECT "Plant", "Date", row_number() OVER (PARTITION BY "Plant" ORDER BY "Date") AS "Row"FROM "Generation History"ORDER BY 1, 2;Das Ergebnis ist:
| Plant | Date | Row |
|---|---|---|
| Boston | 2019-01-02 | 1 |
| Boston | 2019-01-03 | 2 |
| Boston | 2019-01-04 | 3 |
| … | … | … |
| Worcester | 2019-01-02 | 1 |
| Worcester | 2019-01-03 | 2 |
| Worcester | 2019-01-04 | 3 |
| … | … | … |
Beachten Sie, dass das Ergebnis selbst dann nicht sortiert sein muss,
wenn die Funktion mit einer ORDER BY-Klausel berechnet wird;
das SELECT muss also explizit sortiert werden, wenn das gewünscht ist.
Framing
Framing legt eine Menge von Zeilen relativ zu jeder Zeile fest, an der die Funktion ausgewertet wird.
Die Distanz von der aktuellen Zeile wird als Ausdruck entweder PRECEDING oder FOLLOWING der aktuellen Zeile in der durch die ORDER BY-Klausel in der OVER-Spezifikation angegebenen Ordnung angegeben.
Diese Distanz kann entweder als ganzzahlige Anzahl von ROWS oder GROUPS
oder als RANGE-Delta-Ausdruck angegeben werden. Es ist ungültig, wenn ein Rahmen nach seinem Ende beginnt.
Für eine RANGE-Spezifikation darf es nur einen Ordnungsausdruck geben, und er muss Subtraktion unterstützen, es sei denn, es werden nur die Sentinel-Grenzwerte UNBOUNDED PRECEDING / UNBOUNDED FOLLOWING / CURRENT ROW verwendet.
Mit der EXCLUDE-Klausel können Zeilen, die im angegebenen Ordnungsausdruck gleich der aktuellen Zeile sind (sogenannte Peers), aus dem Rahmen ausgeschlossen werden.
Der Standardrahmen ist unbegrenzt (d. h. die gesamte Partition), wenn keine ORDER BY-Klausel vorhanden ist, und RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, wenn eine ORDER BY-Klausel vorhanden ist. Standardmäßig bedeutet der Grenzwert CURRENT ROW (aber nicht das CURRENT ROW in der EXCLUDE-Klausel) die aktuelle Zeile und alle ihre Peers, wenn RANGE- oder GROUP-Framing verwendet wird, aber nur die aktuelle Zeile, wenn ROWS-Framing verwendet wird.
ROWS-Framing
Hier ist eine einfache ROW-Rahmenabfrage mit einer Aggregatfunktion:
SELECT points, sum(points) OVER ( ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS weFROM results;Diese Abfrage berechnet die sum jedes Punkts und der Punkte zu beiden Seiten:
Beachten Sie, dass am Rand der Partition nur zwei Werte addiert werden. Das liegt daran, dass Rahmen an den Rand der Partition beschnitten werden.
RANGE-Framing
Zurück zu den Stromdaten: Angenommen, die Daten sind verrauscht. Wir möchten vielleicht einen 7-Tage-gleitenden Durchschnitt für jedes Kraftwerk berechnen, um das Rauschen zu glätten. Dazu können wir diese Fensterabfrage verwenden:
SELECT "Plant", "Date", avg("MWh") OVER ( PARTITION BY "Plant" ORDER BY "Date" ASC RANGE BETWEEN INTERVAL 3 DAYS PRECEDING AND INTERVAL 3 DAYS FOLLOWING) AS "MWh 7-day Moving Average"FROM "Generation History"ORDER BY 1, 2;Diese Abfrage partitioniert die Daten nach Plant (um die Daten der verschiedenen Kraftwerke getrennt zu halten),
ordnet die Partition jedes Kraftwerks nach Date (um die Energiemessungen nebeneinander zu legen)
und verwendet einen RANGE-Rahmen von drei Tagen auf beiden Seiten jedes Tages für den avg
(um fehlende Tage zu behandeln).
Das ist das Ergebnis:
| Plant | Date | MWh 7-day Moving Average |
|---|---|---|
| Boston | 2019-01-02 | 517450.75 |
| Boston | 2019-01-03 | 508793.20 |
| Boston | 2019-01-04 | 508529.83 |
| … | … | … |
| Boston | 2019-01-13 | 499793.00 |
| Worcester | 2019-01-02 | 104768.25 |
| Worcester | 2019-01-03 | 102713.00 |
| Worcester | 2019-01-04 | 102249.50 |
| … | … | … |
GROUPS-Framing
Die dritte Framing-Art zählt Gruppen von Zeilen relativ zur aktuellen Zeile.
Eine Gruppe in diesem Framing ist eine Menge von Werten mit identischen ORDER BY-Werten.
Wenn wir annehmen, dass an jedem Tag Strom erzeugt wird,
können wir GROUPS-Framing verwenden, um den gleitenden Durchschnitt allen im System erzeugten Stroms zu berechnen,
ohne Datumsarithmetik zu brauchen:
SELECT "Date", "Plant", avg("MWh") OVER ( ORDER BY "Date" ASC GROUPS BETWEEN 3 PRECEDING AND 3 FOLLOWING) AS "MWh 7-day Moving Average"FROM "Generation History"ORDER BY 1, 2;| Date | Plant | MWh 7-day Moving Average |
|---|---|---|
| 2019-01-02 | Boston | 311109.500 |
| 2019-01-02 | Worcester | 311109.500 |
| 2019-01-03 | Boston | 305753.100 |
| 2019-01-03 | Worcester | 305753.100 |
| 2019-01-04 | Boston | 305389.667 |
| 2019-01-04 | Worcester | 305389.667 |
| … | … | … |
| 2019-01-12 | Boston | 309184.900 |
| 2019-01-12 | Worcester | 309184.900 |
| 2019-01-13 | Boston | 299469.375 |
| 2019-01-13 | Worcester | 299469.375 |
Beachten Sie, dass die Werte für jedes Datum gleich sind.
EXCLUDE-Klausel
EXCLUDE ist ein optionaler Modifikator der Rahmenklausel, um Zeilen um die CURRENT ROW auszuschließen.
Das ist nützlich, wenn Sie einen Aggregatwert benachbarter Zeilen berechnen möchten,
um zu sehen, wie die aktuelle Zeile damit verglichen wird.
Im folgenden Beispiel möchten wir wissen, wie die Zeit eines Athleten in einem Wettkampf im Vergleich zum Durchschnitt aller für ihren Wettkampf innerhalb von ±10 Tagen erfassten Zeiten steht:
SELECT event, date, athlete, avg(time) OVER w AS recent,FROM resultsWINDOW w AS ( PARTITION BY event ORDER BY date RANGE BETWEEN INTERVAL 10 DAYS PRECEDING AND INTERVAL 10 DAYS FOLLOWING EXCLUDE CURRENT ROW)ORDER BY event, date, athlete;Es gibt vier Optionen für EXCLUDE, die festlegen, wie die aktuelle Zeile behandelt wird:
CURRENT ROW– nur die aktuelle Zeile ausschließenGROUP– die aktuelle Zeile und alle ihre „Peers“ ausschließen (Zeilen mit demselbenORDER BY-Wert)TIES– alle Peer-Zeilen ausschließen, aber nicht die aktuelle Zeile (das erzeugt ein Loch auf beiden Seiten)NO OTHERS– nichts ausschließen (der Standard)
Der Ausschluss ist sowohl für Fensteraggregate als auch für die Funktionen first, last und nth_value implementiert.
WINDOW-Klauseln
Mehrere unterschiedliche OVER-Klauseln können im selben SELECT angegeben werden, und jede wird getrennt berechnet.
Oft möchten wir jedoch dasselbe Layout für mehrere Fensterfunktionen verwenden.
Die WINDOW-Klausel kann verwendet werden, um ein benanntes Fenster zu definieren, das zwischen mehreren Fensterfunktionen geteilt werden kann:
SELECT "Plant", "Date", min("MWh") OVER seven AS "MWh 7-day Moving Minimum", avg("MWh") OVER seven AS "MWh 7-day Moving Average", max("MWh") OVER seven AS "MWh 7-day Moving Maximum"FROM "Generation History"WINDOW seven AS ( PARTITION BY "Plant" ORDER BY "Date" ASC RANGE BETWEEN INTERVAL 3 DAYS PRECEDING AND INTERVAL 3 DAYS FOLLOWING)ORDER BY 1, 2;Die drei Fensterfunktionen teilen sich außerdem das Datenlayout, was die Leistung verbessert.
Mehrere Fenster können in derselben WINDOW-Klausel durch Kommas getrennt definiert werden:
SELECT "Plant", "Date", min("MWh") OVER seven AS "MWh 7-day Moving Minimum", avg("MWh") OVER seven AS "MWh 7-day Moving Average", max("MWh") OVER seven AS "MWh 7-day Moving Maximum", min("MWh") OVER three AS "MWh 3-day Moving Minimum", avg("MWh") OVER three AS "MWh 3-day Moving Average", max("MWh") OVER three AS "MWh 3-day Moving Maximum"FROM "Generation History"WINDOW seven AS ( PARTITION BY "Plant" ORDER BY "Date" ASC RANGE BETWEEN INTERVAL 3 DAYS PRECEDING AND INTERVAL 3 DAYS FOLLOWING), three AS ( PARTITION BY "Plant" ORDER BY "Date" ASC RANGE BETWEEN INTERVAL 1 DAYS PRECEDING AND INTERVAL 1 DAYS FOLLOWING)ORDER BY 1, 2;Die Abfragen oben verwenden eine Reihe von Klauseln nicht, die in Select-Anweisungen üblich sind, etwa
WHERE, GROUP BY usw. Für komplexere Abfragen finden Sie, wo WINDOW-Klauseln in der
kanonischen Reihenfolge der SELECT-Anweisung stehen.
Ergebnisse von Fensterfunktionen mit QUALIFY filtern
Fensterfunktionen werden ausgeführt, nachdem die Klauseln WHERE und HAVING bereits ausgewertet wurden; es ist daher nicht möglich, diese Klauseln zu verwenden, um die Ergebnisse von Fensterfunktionen zu filtern.
Die QUALIFY-Klausel erspart eine Unterabfrage oder WITH-Klausel für diese Filterung.
Box-and-Whisker-Abfragen
Alle Aggregate können als Fensterfunktionen verwendet werden, einschließlich der komplexen statistischen Funktionen. Diese Funktionsimplementierungen wurden für das Fenstern optimiert, und wir können die Fenstersyntax verwenden, um Abfragen zu schreiben, die die Daten für gleitende Box-and-Whisker-Plots erzeugen:
SELECT "Plant", "Date", min("MWh") OVER seven AS "MWh 7-day Moving Minimum", quantile_cont("MWh", [0.25, 0.5, 0.75]) OVER seven AS "MWh 7-day Moving IQR", max("MWh") OVER seven AS "MWh 7-day Moving Maximum",FROM "Generation History"WINDOW seven AS ( PARTITION BY "Plant" ORDER BY "Date" ASC RANGE BETWEEN INTERVAL 3 DAYS PRECEDING AND INTERVAL 3 DAYS FOLLOWING)ORDER BY 1, 2;