23rd Dec 2021 9 Leseminuten Wie verwendet man die SQL PARTITION BY-Klausel mit OVER? Ignacio L. Bisso Window Functions Inhaltsverzeichnis Was ist die PARTITION BY-Klausel in SQL? Vertiefung mit der SQL-Klausel PARTITION BY Die SQL PARTITION BY-Klausel in Aktion Beispiel 1 Beispiel 2 Beispiel 3 Die Leistungsfähigkeit von Fensterfunktionen und der SQL PARTITION BY Das Herzstück jedes Aufrufs einer Fensterfunktion ist eine OVER-Klausel, die definiert, wie die Fenster der Datensätze aufgebaut sind. Innerhalb der OVER-Klausel kann es eine optionale PARTITION BY-Subklausel geben, die die Kriterien zur Identifizierung der in jedes Fenster aufzunehmenden Datensätze definiert. Lesen Sie weiter und machen Sie einen wichtigen Schritt zur Erweiterung Ihrer SQL-Kenntnisse! Was ist die PARTITION BY-Klausel in SQL? Der SQL-Ausdruck PARTITION BY ist eine Unterklausel der Klausel OVER, die in fast allen Aufrufen von Fensterfunktionen wie AVG(), MAX() und RANK() verwendet wird. Wie viele Leser wahrscheinlich wissen, arbeiten Fensterfunktionen mit Fensterrahmen, d. h. mit Gruppen von Zeilen, die für jeden Datensatz im Abfrageergebnis unterschiedlich sein können. Hier kommt die Unterklausel SQL PARTITION BY ins Spiel: Sie wird verwendet, um zu definieren, welche Datensätze Teil des Fensterrahmens sein sollen, der mit jedem Datensatz des Ergebnisses verbunden ist. Dieser Artikel erklärt die SQL PARTITION BY und ihre Verwendung anhand von Beispielen. Da er eng mit den Fensterfunktionen zusammenhängt, sollten Sie zunächst einige Artikel über Fensterfunktionen lesen, z. B. "SQL Window Function Example With Explanations", wo Sie viele Beispiele finden. Wenn Sie mehr über Fensterfunktionen erfahren möchten, gibt es auch einen interessanten Artikel mit vielen Verweisen auf andere Artikel über Fensterfunktionen. Das erste, worauf Sie achten sollten, ist die Syntax. Hier sehen Sie, wie Sie die SQL-Klausel PARTITION BY verwenden: SELECT , OVER(PARTITION BY [ORDER BY ]) FROM table; Schauen wir uns nun ein Beispiel an, das eine PARTITION BY Klausel verwendet. Wir werden die folgende Tabelle verwenden, die car_list_prices: car_makecar_modelcar_typecar_price FordMondeopremium18200 RenaultFuegosport16500 CitroenCactuspremium19000 FordFalconlow cost8990 FordGalaxystandard12400 RenaultMeganestandard14300 CitroenPicassopremium23400 Für jedes Auto wollen wir die Marke, das Modell, den Preis, den Durchschnittspreis über alle Autos und den Durchschnittspreis über denselben Autotyp (um eine bessere Vorstellung davon zu bekommen, wie der Preis eines bestimmten Autos im Vergleich zu anderen Autos ist) erhalten. Hier ist die Abfrage: SELECT car_make, car_model, car_price, AVG(car_price) OVER() AS "overall average price", AVG(car_price) OVER (PARTITION BY car_type) AS "car type average price" FROM car_list_prices Das Ergebnis der Abfrage ist das folgende: car_makecar_modelcar_priceoverall average pricecar type average price FordMondeo1820016112.858990.00 RenaultFuego1650016112.8520200.00 CitroenCactus1900016112.8520200.00 FordFalcon899016112.8520200.00 FordGalaxy1240016112.8516500.00 RenaultMegane1430016112.8513350.00 CitroenPicasso2340016112.8513350.00 Die obige Abfrage verwendet zwei Fensterfunktionen. Die erste wird verwendet, um den Durchschnittspreis für alle Fahrzeuge in der Preisliste zu berechnen. Sie verwendet die Fensterfunktion AVG() mit einer leeren OVER Klausel, wie im folgenden Ausdruck zu sehen ist: AVG(car_price) OVER() AS "overall average price" Die zweite Fensterfunktion wird verwendet, um den Durchschnittspreis für ein bestimmtes car_type wie Standard, Premium, Sport usw. zu berechnen. Hier verwenden wir eine OVER Klausel mit einer PARTITION BY Subklausel, wie in diesem Ausdruck zu sehen ist: AVG(car_price) OVER (PARTITION BY car_type) AS "car type average price" Die Fensterfunktionen sind ziemlich mächtig, nicht wahr? Wenn Sie mehr lernen möchten, indem Sie gut vorbereitete Übungen machen, empfehle ich Ihnen den Kurs Fensterfunktionenzu empfehlen, in dem Sie die Verwendung von Fensterfunktionen in SQL-Datenbanken kennenlernen und sich damit vertraut machen können. Vertiefung mit der SQL-Klausel PARTITION BY Die GROUP BY Klausel gruppiert eine Reihe von Datensätzen anhand von Kriterien. Dadurch können wir eine Funktion (z. B. AVG() oder MAX()) auf Gruppen von Datensätzen anwenden, um ein Ergebnis pro Gruppe zu erhalten. Ein Beispiel: Sie möchten den Durchschnittspreis und den Höchstpreis für jede Marke ermitteln. Verwenden Sie die folgende Abfrage: SELECT car_make, AVG(car_price) AS average_price, MAX(car_price) AS top_price FROM car_list_prices GROUP BY car_make Hier ist das Ergebnis dieser Abfrage: car_makeaverage_pricetop_price Ford1319618200 Renault1540016500 Citroen2120023400 Im Vergleich zu Fensterfunktionen fasst GROUP BY einzelne Datensätze zu einer Gruppe zusammen. Folglich können Sie sich nicht auf ein einzelnes Datensatzfeld beziehen, sondern nur auf die Spalten in der GROUP BY Klausel. Angenommen, Sie möchten einen Bericht erstellen, der das Modell, den Preis und den Durchschnittspreis der Marke enthält. Dies ist mit GROUP BY nicht möglich, da die einzelnen Datensätze jedes Modells aufgrund der Klausel GROUP BY car_make zusammengeklappt werden. Für so etwas müssen Sie Fensterfunktionen verwenden, wie im folgenden Beispiel zu sehen ist: SELECT car_make, car_model, car_price, AVG(car_price) OVER (PARTITION BY car_make) AS average_make FROM car_list_prices Das Ergebnis dieser Abfrage ist das folgende: car_makecar_modelcar_priceaverage_make CitroenPicasso2340021200 CitroenCactus1900021200 FordGalaxy1240013196 FordFalcon899013196 FordMondeo1820013196 RenaultMegane1430015400 RenaultFuego1650015400 Wer tiefer einsteigen möchte, dem empfehle ich den Artikel "What Is the Difference Between a GROUP BY and a PARTITION BY?" mit vielen Beispielen unter Verwendung von Aggregat- und Fensterfunktionen. Zusätzlich zur Klausel PARTITION BY gibt es eine weitere Klausel namens ORDER BY, die die Reihenfolge der Datensätze innerhalb des Fensterrahmens festlegt. Für einige Fensterfunktionen ist eine ORDER BY erforderlich. Die Fensterfunktionen LEAD() und LAG() benötigen beispielsweise ein geordnetes Datensatzfenster, da sie auf den vorangehenden oder den nächsten Datensatz des aktuellen Datensatzes zugreifen. Ein Fensterrahmen besteht aus mehreren Zeilen, die durch die Kriterien in der Klausel PARTITION BY definiert sind. Wir können jedoch auch Grenzen für den Fensterrahmen angeben, wie in der folgenden Abbildung zu sehen ist: Die unteren und oberen Grenzen in der OVER Klausel können sein: UNBOUNDED PRECEDING n PRECEDING CURRENT ROW n FOLLOWING UNBOUNDED FOLLOWING Wenn in einer OVER -Klausel keine Begrenzung angegeben wird, wird der Fensterrahmen auf der Grundlage einiger Standardgrenzwerte erstellt. Diese hängen von der Syntax ab, die für den Aufruf der Fensterfunktion verwendet wird. Die folgende Tabelle zeigt die Standardbegrenzungen des Fensterrahmens. Syntax usedFirst Row in WindowLast Row in Window Just empty OVER() clauseUNBOUNDED PRECEDINGUNBOUNDED FOLLOWING OVER(PARTITION BY …)UNBOUNDED PRECEDINGUNBOUNDED FOLLOWING OVER(PARTITION BY … ORDER BY …)UNBOUNDED PRECEDINGCURRENT ROW Es gibt einen ausführlichen Artikel mit dem Titel "SQL Fensterfunktionen Cheat Sheet", in dem Sie viele Details zur Syntax und Beispiele zu den verschiedenen Begrenzungen des Fensterrahmens finden können. Die SQL PARTITION BY-Klausel in Aktion In diesem Abschnitt zeigen wir einige Beispiele für die SQL-Klausel PARTITION BY. Sie basieren alle auf der Tabelle paris_london_flights, die von einer Fluggesellschaft verwendet wird, um die Geschäftsergebnisse dieser Strecke für die Jahre 2018 und 2019 zu analysieren. Hier ist eine Teilmenge der Daten: aircraft_makeaircarft_modelflight_numberscheduled_departurereal_departurescheduled_arrivalnum_of_passengerstotal_revenue Boeing757 300FLP0032019-01-30 15:00:002019-01-30 15:00:002019-01-30 15:00:0026082630.10 Boeing737 200FLP0032019-02-01 15:00:002019-02-01 15:10:002019-02-01 15:55:0019558459.34 AirbusA500FLP0032019-02-01 15:00:002019-02-01 15:03:002019-02-01 15:03:5531291570.87 AirbusA500FLP0012019-10-28 05:00:002019-10-28 05:04:002019-10-28 05:55:0029887943.00 Boeing737 200FLP0022019-10-28 09:00:002019-10-28 09:00:002019-10-28 09:55:0017856342.45 Beispiel 1 Die erste Abfrage erzeugt einen Bericht, der die flight_number, aircraft_model mit der Anzahl der beförderten Passagiere und den Gesamteinnahmen enthält. Die Abfrage lautet wie folgt: SELECT DISTINCT flight_number, aircraft_model, SUM(num_of_passengers) OVER (PARTITION BY flight_number, aircraft_model) AS total_passengers, SUM(total_revenue) OVER (PARTITION BY flight_number, aircraft_model) AS total_revenue FROM paris_london_flights ORDER BY flight_number, aircraft_model; Da die Gesamtzahl der beförderten Passagiere und die Gesamteinnahmen für jede mögliche Kombination von flight_number und aircraft_model generiert werden, verwenden wir die folgende PARTITION BY Klausel, um eine Gruppe von Datensätzen mit derselben Flugnummer und demselben Flugzeugmodell zu erzeugen: OVER (PARTITION BY flight_number, aircraft_model) Dann wenden wir für jeden Satz von Datensätzen die Fensterfunktionen SUM(num_of_passengers) und SUM(total_revenue) an, um die in der nächsten Ergebnismenge gezeigten Metriken total_passengers und total_revenue zu erhalten. flight_numberaircraft_modeltotal_passengerstotal_revenue FLP001737 200204816016060.82 FLP001757 300183895361126.23 FLP001Airbus A5005387215892165.58 FLP002737 200216606297197.71 FLP002757 300168694951475.86 FLP002Airbus A5005462716004812.16 FLP003737 200200985874892.44 FLP003757 300157084573379.28 FLP003Airbus A5005753316712475.04 Beispiel 2 In der nächsten Abfrage zeigen wir, wie sich das Geschäft entwickelt, indem wir die Metriken eines Monats mit denen des Vormonats vergleichen. Wir erstellen einen Bericht mit Hilfe von Fensterfunktionen, um die monatlichen Veränderungen bei den Fahrgästen und den Einnahmen zu zeigen. WITH year_month_data AS ( SELECT DISTINCT EXTRACT(YEAR FROM scheduled_departure) AS year, EXTRACT(MONTH FROM scheduled_departure) AS month, SUM(number_of_passengers) OVER (PARTITION BY EXTRACT(YEAR FROM scheduled_departure), EXTRACT(MONTH FROM scheduled_departure) ) AS passengers FROM paris_london_flights ORDER BY 1, 2 ) SELECT year, month, passengers, LAG(passengers) OVER (ORDER BY year, month) passengers_previous_month, passengers - LAG(passengers) OVER (ORDER BY year, month) AS passengers_delta FROM year_month_data; In der obigen Abfrage verwenden wir eine WITH -Klausel, um eine CTE zu generieren (CTE steht für Common Table Expressions und ist eine Art von Abfrage zur Generierung einer virtuellen Tabelle, die im Rest der Abfrage verwendet werden kann). Wir füllen die Daten in eine virtuelle Tabelle namens year_month_data, die 3 Spalten hat: year, month und passengers mit der Gesamtzahl der beförderten Passagiere im Monat. Dann erzeugt die zweite Abfrage (die die CTE year_month_data als Eingabe verwendet) das Ergebnis der Abfrage. Die Spalte Passagiere enthält die Gesamtzahl der beförderten Passagiere, die mit dem aktuellen Datensatz verbunden sind. Mit der Fensterfunktion LAG(passenger) erhalten wir den Wert der Spalte Passagiere des vorherigen Datensatzes zum aktuellen Datensatz. Wir ORDER BY year and month: LAG(passengers) OVER (ORDER BY year, month) passengers_previous_month Er erhält die Anzahl der Fahrgäste aus dem vorherigen Datensatz, der dem Vormonat entspricht. Anschließend wird die Zahl der Fahrgäste für den aktuellen und den vorangegangenen Monat angezeigt. In der letzten Spalte wird schließlich die Differenz zwischen beiden Werten berechnet, um die monatliche Veränderung der Fahrgastzahlen zu erhalten. yearmonthpassengerspassengers_previous_monthpassengers_delta 20181211469nullnull 20191247231146913254 201922253624723-2187 2019324994225362458 201942440824994-586 201952399824408-410 201962379323998-205 2019724816237931023 201982433424816-482 201992371924334-615 20191024989237191270 2019112437124989-618 201912108724371-23284 Beispiel 3 In unserem letzten Beispiel betrachten wir die Flugverspätungen. Wir wollen verschiedene Durchschnittswerte für Verspätungen erhalten, um die Gründe für die Verspätungen zu erklären. Wir verwenden eine CTE, um eine Spalte namens month_delay mit der durchschnittlichen Verspätung für jeden Monat zu berechnen und das Flugzeugmodell zu erhalten. In der Hauptabfrage erhalten wir dann die verschiedenen Durchschnittswerte wie unten dargestellt: WITH paris_london_delays AS ( SELECT DISTINCT aircraft_model, EXTRACT(YEAR FROM scheduled_departure) AS year, EXTRACT(MONTH FROM scheduled_departure) AS month, AVG(real_departure - scheduled_departure) AS month_delay FROM paris_london_flights GROUP BY 1, 2, 3 ) SELECT DISTINCT aircraft_model, year, month, month_delay AS monthly_avg_delay, AVG(month_delay) OVER (PARTITION BY aircraft_model, year) AS year_avg_delay, AVG(month_delay) OVER (PARTITION BY year) AS year_avg_delay_all_models, AVG(month_delay) OVER (PARTITION BY aircraft_model, year ORDER BY month ROWS BETWEEN 3 PRECEDING AND CURRENT ROW ) AS rolling_average_last_4_months FROM paris_london_delays ORDER BY 1,2,3 Diese Abfrage berechnet mehrere Durchschnittswerte. Der erste ist der Durchschnitt pro Flugzeugmodell und Jahr, was sehr eindeutig ist. Der zweite ist der Durchschnitt pro Jahr über alle Flugzeugmodelle. Beachten Sie, dass wir nur die Spalte Jahr in der PARTITION BY Klausel verwenden. Der dritte und letzte Durchschnitt ist der gleitende Durchschnitt, bei dem wir die letzten drei Monate und den aktuellen Monat (d. h. die Zeile) verwenden, um den Durchschnitt mit dem folgenden Ausdruck zu berechnen: AVG(month_delay) OVER (PARTITION BY aircraft_model, year ORDER BY month ROWS BETWEEN 3 PRECEDING AND CURRENT ROW ) AS rolling_average_last_4_months Die Klausel ROWS BETWEEN 3 PRECEDING AND CURRENT ROW in PARTITION BY schränkt die Anzahl der Zeilen (d. h. Monate) ein, die in den Durchschnitt einbezogen werden: die letzten 3 Monate und der aktuelle Monat. Ein Teilergebnis dieser Abfrage sehen Sie unten: aircraft_modelyearmonthmonth_delayyear_avg_delayyear_avg_delay_all_modelsrolling_average_last_4_months 737 20020181200:02:13.8400:02:13.8400:03:13.7000:02:13.84 737 2002019100:02:16.8000:02:36.5900:02:34.1200:02:16.80 737 2002019200:02:35.0000:02:36.5900:02:34.1200:02:25.90 737 2002019300:01:38.4000:02:36.5900:02:34.1200:02:10.06 737 2002019400:04:00.0000:02:36.5900:02:34.1200:02:37.55 737 2002019500:03:12.7200:02:36.5900:02:34.1200:02:51.53 737 2002019600:02:21.4200:02:36.5900:02:34.1200:02:48.13 Der Artikel "Die RANGE-Klausel in SQL Fensterfunktionen: 5 praktische Beispiele" erklärt, wie man eine Teilmenge von Zeilen im Fensterrahmen mit Hilfe von RANGE anstelle von ROWS definiert, mit mehreren Beispielen. Ein weiterer interessanter Artikel ist "Common SQL Fensterfunktionen: Using Partitions With Ranking Functions", in dem die PARTITION BY-Klausel im Detail behandelt wird. Die Leistungsfähigkeit von Fensterfunktionen und der SQL PARTITION BY Fensterfunktionen sind eine sehr mächtige Ressource der SQL-Sprache, und die SQL PARTITION BY Klausel spielt eine zentrale Rolle bei ihrer Verwendung. In diesem Artikel haben wir uns mit der Funktionsweise dieser Klausel beschäftigt und mehrere Beispiele mit unterschiedlichen Syntaxen gezeigt. Bevor ich schließe, schlage ich einen Fortgeschrittenes SQL Kurs, in dem Sie über die Grundlagen hinausgehen und ein SQL-Meister werden können. Wenn Sie mehr über die OVER-Klausel lesen möchten, gibt es einen vollständigen Artikel zum Thema: "How to Define a Window Frame in SQL Fensterfunktionen." Verbessern Sie Ihre Fähigkeiten und erweitern Sie Ihr Vermögen! Tags: Window Functions