Eine Einführung in SQL-Fensterfunktionen
Veröffentlicht: 2022-03-11Die sehr mächtige Funktion, die Sie gerne hassen (aber wissen müssen)
SQL-Fensterfunktionen bieten einige äußerst leistungsstarke und nützliche Funktionen. Aber für viele sind sie, da sie dem Standard-SQL so fremd sind, schwer zu lernen und zu verstehen, haben eine seltsame Syntax – und werden sehr oft vermieden.
Fensterfunktionen können einfach als Berechnungsfunktionen erklärt werden, die der Aggregation ähneln, aber wo die normale Aggregation über die GROUP BY
-Klausel dann die einzelnen zu aggregierenden Zeilen verbirgt, haben Fensterfunktionen Zugriff auf einzelne Zeilen und können einige der Attribute aus diesen Zeilen in die hinzufügen Ergebnissatz.
In diesem Tutorial zu SQL-Fensterfunktionen werde ich Ihnen den Einstieg in Fensterfunktionen erleichtern, die Vorteile erläutern und erklären, wann Sie sie verwenden, und Ihnen echte Beispiele geben, die Ihnen bei den Konzepten helfen.
Ein Fenster in Ihre Daten
Eine der am häufigsten verwendeten und wichtigsten Funktionen in SQL ist die Möglichkeit, Datenzeilen auf bestimmte Weise zu aggregieren oder zu gruppieren. In einigen Fällen kann die Gruppierung jedoch sehr komplex werden, je nachdem, was erforderlich ist.
Wollten Sie schon immer die Ergebnisse Ihrer Abfrage durchlaufen, um ein Ranking, eine Top-X-Liste oder ähnliches zu erhalten? Hatten Sie Analytics-Projekte, bei denen Sie Ihre Daten genau richtig für ein Visualisierungstool aufbereiten wollten, es aber fast unmöglich oder so komplex fanden, dass es sich nicht gelohnt hat?
Fensterfunktionen können die Dinge erleichtern. Nachdem Sie das Ergebnis Ihrer Abfrage erhalten haben – dh nach der WHERE
-Klausel und jeder standardmäßigen Aggregation – wirken Fensterfunktionen auf die verbleibenden Zeilen (das Datenfenster ) und erhalten, was Sie wollen.
Einige der Fensterfunktionen, die wir uns ansehen werden, sind:
-
OVER
-
COUNT()
-
SUM()
-
ROW_NUMBER()
-
RANK()
-
DENSE_RANK()
-
LEAD()
-
LAG()
Über Einfach
Die OVER
Klausel gibt eine Fensterfunktion an und muss immer in der Anweisung enthalten sein. Der Standardwert in einer OVER
-Klausel ist das gesamte Rowset. Sehen wir uns als Beispiel eine Mitarbeitertabelle in einer Unternehmensdatenbank an und zeigen die Gesamtzahl der Mitarbeiter in jeder Zeile zusammen mit den Informationen zu jedem Mitarbeiter, einschließlich des Zeitpunkts, zu dem er bei dem Unternehmen angefangen hat.
SELECT COUNT(*) OVER() As NumEmployees, firstname, lastname, date_started FROM Employee ORDER BY date_started;
AnzahlMitarbeiter | Vorname | Familienname, Nachname | Datum gestartet |
---|---|---|---|
3 | John | Schmied | 01.01.2019 00:00:00.000 |
3 | Ausfall | Jones | 2019-02-15 00:00:00.000 |
3 | Sam | Gordon | 2019-02-18 00:00:00.000 |
Das Obige kann, wie viele Fensterfunktionen, auch auf eine vertrautere Weise ohne Fenster geschrieben werden – was in diesem einfachen Beispiel nicht allzu schlimm ist:
SELECT (SELECT COUNT(*) FROM Employee) as NumEmployees, firstname, lastname, date_started FROM Employee ORDER BY date_started;
Nehmen wir nun an, wir möchten die Anzahl der Mitarbeiter anzeigen, die im selben Monat wie der Mitarbeiter in der Reihe angefangen haben. Wir müssen die Anzahl für jede Zeile auf genau diesen Monat eingrenzen oder einschränken. Wie wird das gemacht? Wir verwenden die PARTITION
Klausel des Fensters wie folgt:
SELECT COUNT(*) OVER (PARTITION BY MONTH(date_started),YEAR(date_started)) As NumPerMonth, DATENAME(month,date_started)+' '+DATENAME(year,date_started) As TheMonth, firstname, lastname FROM Employee ORDER BY date_started;
AnzahlProMonat | Der Monat | Vorname | Familienname, Nachname |
1 | Januar 2019 | John | Schmied |
2 | Februar 2019 | Ausfall | Jones |
2 | Februar 2019 | Sam | Gordon |
Partitionen ermöglichen es Ihnen, das Fenster nach einem bestimmten Wert oder bestimmten Werten in Abschnitte zu filtern. Jeder Abschnitt wird oft als Fensterrahmen bezeichnet.
Nehmen wir weiter an, wir wollten nicht nur herausfinden, wie viele Mitarbeiter im selben Monat angefangen haben, sondern auch zeigen, in welcher Reihenfolge sie in diesem Monat angefangen haben. Dafür können wir die bekannte ORDER BY
Klausel verwenden. Innerhalb einer Fensterfunktion verhält sich ORDER BY
jedoch etwas anders als am Ende einer Abfrage.
SELECT COUNT(*) OVER (PARTITION BY MONTH(date_started), YEAR(date_started) ORDER BY date_started) As NumThisMonth, DATENAME(month,date_started)+' '+DATENAME(year,date_started) As TheMonth, firstname, lastname, date_started FROM Employee ORDER BY date_started;
AnzahlDieserMonat | Der Monat | Vorname | Familienname, Nachname |
1 | Januar 2019 | John | Schmied |
1 | Februar 2019 | Ausfall | Jones |
2 | Februar 2019 | Sam | Gordon |
In diesem Fall ändert ORDER BY
das Fenster so, dass es vom Beginn der Partition (in diesem Fall Monat und Jahr, in dem der Mitarbeiter begonnen hat) bis zur aktuellen Zeile reicht. Somit beginnt die Zählung bei jeder Partition neu.
Ordnen Sie es
Fensterfunktionen können für Ranking-Zwecke sehr nützlich sein. Zuvor haben wir gesehen, dass die Verwendung der COUNT
-Aggregationsfunktion es uns ermöglichte, zu sehen, in welcher Reihenfolge Mitarbeiter dem Unternehmen beigetreten sind. Wir hätten auch Window-Ranking-Funktionen wie ROW_NUMBER()
, RANK()
und DENSE_RANK()
.

Die Unterschiede werden sichtbar, nachdem wir im folgenden Monat einen neuen Mitarbeiter hinzugefügt und die Partition entfernt haben:
SELECT ROW_NUMBER() OVER (ORDER BY YEAR(date_started),MONTH(date_started)) As StartingRank, RANK() OVER (ORDER BY YEAR(date_started),MONTH(date_started)) As EmployeeRank, DENSE_RANK() OVER (ORDER BY YEAR(date_started),MONTH(date_started)) As DenseRank, DATENAME(month,date_started)+' '+DATENAME(year,date_started) As TheMonth, firstname, lastname, date_started FROM Employee ORDER BY date_started;
Startrang | MitarbeiterRang | DenseRank | Der Monat | Vorname | Familienname, Nachname | Datum gestartet |
1 | 1 | 1 | Januar 2019 | John | Schmied | 01.01.2019 |
2 | 2 | 2 | Februar 2019 | Ausfall | Jones | 2019-02-15 |
3 | 2 | 2 | Februar 2019 | Sam | Gordon | 2019-02-18 |
4 | 4 | 3 | März 2019 | Julia | Sánchez | 2019-03-19 |
Sie können die Unterschiede sehen. ROW_NUMBER()
gibt eine sequentielle Zählung innerhalb einer gegebenen Partition (aber ohne Partition geht es durch alle Zeilen). RANK()
gibt den Rang jeder Zeile basierend auf der ORDER BY
Klausel an. Es zeigt Unentschieden an und überspringt dann die nächste Rangliste. DENSE_RANK
zeigt ebenfalls Unentschieden, fährt dann aber mit dem nächsten aufeinanderfolgenden Wert fort, als ob es kein Unentschieden gäbe.
Weitere Ranking-Funktionen sind:
-
CUME_DIST
– Berechnet den relativen Rang der aktuellen Zeile innerhalb einer Partition -
NTILE
– Teilt die Zeilen für jede Fensterpartition so gleichmäßig wie möglich -
PERCENT_RANK
– Prozentrang der aktuellen Zeile
Beachten Sie auch in diesem Beispiel, dass Sie mehrere Fensterfunktionen in einer einzigen Abfrage haben können – und sowohl die Partition als auch die Reihenfolge können in jeder unterschiedlich sein!
Reihen und Bereiche und Rahmen, oh mein Gott
Um Ihren Fensterrahmen innerhalb der OVER()
-Klausel weiter zu definieren oder einzuschränken, können Sie ROWS
und RANGE
. Mit der ROWS
-Klausel können Sie die in Ihrer Partition enthaltenen Zeilen als diejenigen vor oder nach der aktuellen Zeile angeben.
SELECT OrderYear, OrderMonth, TotalDue, SUM(TotalDue) OVER(ORDER BY OrderYear, OrderMonth ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS 'LaggingRunningTotal' FROM sales_products;
In diesem Beispiel geht der Fensterrahmen von der ersten Zeile bis zur aktuellen Zeile minus 1, und die Fenstergröße nimmt für jede Zeile weiter zu.
Die Reichweite funktioniert etwas anders und wir erhalten möglicherweise ein anderes Ergebnis.
SELECT OrderYear, OrderMonth, TotalDue, SUM(TotalDue) OVER(ORDER BY OrderYear, OrderMonth RANGE BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS 'LaggingRunningTotal' FROM sales_products;
Der Bereich umfasst die Zeilen im Fensterrahmen, die dieselben ORDER BY
-Werte wie die aktuelle Zeile haben. Daher ist es möglich, dass Sie mit RANGE
Duplikate erhalten, wenn ORDER BY
nicht eindeutig ist.
Einige beschreiben ROWS
als physikalischen Operator, während RANGE
ein logischer Operator ist. In jedem Fall sind die Standardwerte für ROWS
und RANGE
immer UNBOUNDED PRECEDING AND CURRENT ROW
.
Was sonst?
Die meisten Standard-Aggregatfunktionen arbeiten mit Windows-Funktionen. Wir haben COUNT
bereits in den Beispielen gesehen. Andere sind SUM
, AVG
, MIN
, MAX
usw.
Mit Fensterfunktionen können Sie auch mit LAG
und LEAD
sowie FIRST_VALUE
und LAST_VALUE
sowohl auf vorherige als auch auf nachfolgende Datensätze zugreifen. Angenommen, Sie möchten in jeder Zeile eine Verkaufszahl für den aktuellen Monat und die Differenz zwischen der Verkaufszahl des letzten Monats anzeigen. Sie könnten so etwas tun:
SELECT id, OrderMonth, OrderYear, product, sales, sales - LAG(sales,1) OVER (PARTITION BY product ORDER BY OrderYear, OrderMonth) As sales_change FROM sales_products WHERE sale_year = 2019;
Grundsätzlich sind SQL-Fensterfunktionen sehr leistungsfähig
Obwohl dies eine kurze Einführung in die SQL-Fensterfunktionen ist, wird es hoffentlich Ihr Interesse wecken, zu sehen, was sie alles können. Wir haben gelernt, dass Fensterfunktionen Berechnungen ähnlich wie Aggregationsfunktionen durchführen, aber mit dem zusätzlichen Vorteil, dass sie Zugriff auf Daten innerhalb der einzelnen Zeilen haben, was sie sehr leistungsfähig macht. Sie enthalten immer die OVER
-Klausel und können PARTITION BY
, ORDER BY
und eine Vielzahl von Aggregationsfunktionen ( SUM
, COUNT
usw.) und andere Positionsfunktionen ( LEAD
, LAG
) enthalten. Wir haben auch etwas über Fensterrahmen gelernt und wie sie Datenabschnitte einkapseln.
Beachten Sie, dass verschiedene SQL-Varianten Fensterfunktionen unterschiedlich implementieren können und einige möglicherweise nicht alle Fensterfunktionen oder -Klauseln implementiert haben. Überprüfen Sie unbedingt die Dokumentation für die Plattform, die Sie verwenden.
Wenn Sie als SQL-Entwickler daran interessiert sind, die Leistung Ihrer SQL-Datenbank zu optimieren, sehen Sie sich SQL- Datenbank-Leistungsoptimierung für Entwickler an .
Viel Spaß beim Fenstern!
Weitere Informationen zu bestimmten Implementierungen finden Sie unter:
- PostgreSQL-Dokumentation zu Windows-Funktionen für eine PostgreSQL-Implementierung.
- SELECT - OVER-Klausel (Transact-SQL) -Dokumentation von Microsoft.
- Fensterfunktionen in SQL Server für einen großartigen Überblick über SQL Server-Implementierungen und Teil 2.