Analytische Funktionen
Analytische Funktionen werden typischerweise für folgende Fragestellungen eingesetzt:
- Berechnung der laufenden Summe
- Finde Perzentile innerhalb von Gruppen
- Ermittlung von Top-N …
- Berechnung vom gleitenden Durchschnitt
Analytische Funktionen berechnen einen Gesamtwert anhand einer Gruppe von Zeilen. Sie unterscheiden sich von Aggregatfunktionen, indem sie die Gruppen nicht zusammenfassen, sondern einen Wert für jede Zeile liefern.
Analytische Funktionen werden am Ende der Queryberechnung ausgeführt, aber vor der ORDER BY Klausel. D.h. alle Joins, WHERE, GROUP BY, und HAVING Klauseln werden vor der Berechnung analytischer Funktionen fertiggestellt. Deswegen können analytische Funktionen nur in der SELECT-Liste oder in der ORDER BY Klausel angegeben werden.
Analytische Klausel
Mit Hilfe von PARTITION BY kann die Tabelle in mehrere Partitionen anhand einer angegebenen Bedingung aufgeteilt werden. Das Ergebnis wird für jede Partition separat gerechnet - ähnlich wie bei der GROUP BY Klausel. Ist keine PARTITION BY angegeben, bezieht sich eine analytische Funktion immer auf die ganze Tabelle.
Die ORDER BY Klausel gibt die Sortierreihenfolge der Zeilen innerhalb jeder Partition vor. Bitte beachten Sie, dass diese Reihenfolge sich von der Ausgabereihenfolge unterscheiden kann. Ist diese Klausel angegeben, werden nicht alle Zeilen der Partition in die Berechnung einbezogen, sondern nur ein Teil davon, ein sog. Fenster. In EXASolution besteht dieses Fenster für eine Zeile aus allen Zeilen dieser Partition die entsprechend der Sortierung vor dieser Zeile liegen.
Für die nächsten Beispiele wird die View V_TRANSACTION benutzt, die alle Bestellungen, Retouren und Einzahlungen für jeden Kunden anzeigt.
Beispiel 1: Ohne analytische Klausel
-
SELECT customer_no , last_name , transaction_date 'DATE' , transaction_type TYPE , transaction_sum 'SUM' -- calculates the balance of all customers , SUM(transaction_sum) OVER() total FROM v_transactions ORDER BY last_name, transaction_date;
Das Ergebnis berücksichtigt alle Zeilen der Tabelle, wird im Unterschied zur Aggregatfunktionen für jede Zeile angezeigt!
-
CUS LAST_NAME DATE TYPE SUM TOTAL --- ------------ ---------- ------- -------- -------- 2 Adams 2008-10-11 ORDER -31.32 -77.46 2 Adams 2008-10-25 ORDER -31.89 -77.46 2 Adams 2008-10-25 RETURN 12.64 -77.46 2 Adams 2008-10-27 PAYMENT 6.04 -77.46 2 Adams 2008-11-02 ORDER -8.97 -77.46 2 Adams 2008-11-03 ORDER -19.25 -77.46 2 Adams 2008-11-04 PAYMENT 31.89 -77.46 2 Adams 2008-11-10 RETURN 8.97 -77.46 2 Adams 2008-11-25 ORDER -11.83 -77.46 2 Adams 2008-11-30 PAYMENT 43.72 -77.46 7 Black 2008-10-20 ORDER -21.17 -77.46 7 Black 2008-11-04 PAYMENT 21.17 -77.46 7 Black 2008-11-12 ORDER -17.03 -77.46 7 Black 2008-11-17 PAYMENT 17.03 -77.46 4 Brown 2008-10-19 ORDER -27.49 -77.46 4 Brown 2008-10-25 ORDER -15.13 -77.46 4 Brown 2008-11-03 PAYMENT 27.49 -77.46 4 Brown 2008-11-21 ORDER -21.04 -77.46 4 Brown 2008-11-30 PAYMENT 36.17 -77.46 8 Green 2008-10-14 ORDER -16.48 -77.46 8 Green 2008-11-03 RETURN 8.24 -77.46 8 Green 2008-11-03 PAYMENT 8.24 -77.46 8 Green 2008-11-09 ORDER -43.61 -77.46 3 Jones 2008-10-11 ORDER -42.02 -77.46 3 Jones 2008-10-25 ORDER -34.38 -77.46 3 Jones 2008-11-03 PAYMENT 26.84 -77.46 3 Jones 2008-11-03 RETURN 7.59 -77.46 3 Jones 2008-11-10 ORDER -31.89 -77.46 3 Jones 2008-11-15 PAYMENT 73.86 -77.46 6 Marsch 2008-11-20 ORDER -14.37 -77.46 6 Marsch 2008-12-01 PAYMENT 14.37 -77.46 12 Meier 2008-11-17 ORDER -22.27 -77.46 5 Peters 2008-11-05 ORDER -8.97 -77.46 5 Peters 2008-11-15 ORDER -5.79 -77.46 5 Peters 2008-11-20 PAYMENT 8.97 -77.46 9 White 2008-11-17 ORDER -5.79 -77.46 10 Young 2008-10-12 ORDER -20.50 -77.46 38 rows in resultset.
Beispiel 2: Nutzung von PARTITION BY Klausel
-
SELECT customer_no , last_name , transaction_date 'DATE' , transaction_type TYPE , transaction_sum 'SUM' -- calculates the balance of all customers , SUM(transaction_sum) OVER() total -- calculates the balance of each customer , SUM(transaction_sum) OVER(PARTITION BY customer_no) cust_balance FROM v_transactions ORDER BY last_name, transaction_date;
PARTITION BY customer_no bewirkt die Summenbildung separat für jeden Kunden.
-
CUS LAST_NAME DATE TYPE SUM TOTAL CUST_BAL --- ------------ ---------- ------- -------- -------- -------- 2 Adams 2008-10-11 ORDER -31.32 -77.46 0.00 2 Adams 2008-10-25 ORDER -31.89 -77.46 0.00 2 Adams 2008-10-25 RETURN 12.64 -77.46 0.00 2 Adams 2008-10-27 PAYMENT 6.04 -77.46 0.00 2 Adams 2008-11-02 ORDER -8.97 -77.46 0.00 2 Adams 2008-11-03 ORDER -19.25 -77.46 0.00 2 Adams 2008-11-04 PAYMENT 31.89 -77.46 0.00 2 Adams 2008-11-10 RETURN 8.97 -77.46 0.00 2 Adams 2008-11-25 ORDER -11.83 -77.46 0.00 2 Adams 2008-11-30 PAYMENT 43.72 -77.46 0.00 7 Black 2008-10-20 ORDER -21.17 -77.46 0.00 7 Black 2008-11-04 PAYMENT 21.17 -77.46 0.00 7 Black 2008-11-12 ORDER -17.03 -77.46 0.00 7 Black 2008-11-17 PAYMENT 17.03 -77.46 0.00 4 Brown 2008-10-19 ORDER -27.49 -77.46 0.00 4 Brown 2008-10-25 ORDER -15.13 -77.46 0.00 4 Brown 2008-11-03 PAYMENT 27.49 -77.46 0.00 4 Brown 2008-11-21 ORDER -21.04 -77.46 0.00 4 Brown 2008-11-30 PAYMENT 36.17 -77.46 0.00 8 Green 2008-10-14 ORDER -16.48 -77.46 -43.61 8 Green 2008-11-03 RETURN 8.24 -77.46 -43.61 8 Green 2008-11-03 PAYMENT 8.24 -77.46 -43.61 8 Green 2008-11-09 ORDER -43.61 -77.46 -43.61 3 Jones 2008-10-11 ORDER -42.02 -77.46 0.00 3 Jones 2008-10-25 ORDER -34.38 -77.46 0.00 3 Jones 2008-11-03 PAYMENT 26.84 -77.46 0.00 3 Jones 2008-11-03 RETURN 7.59 -77.46 0.00 3 Jones 2008-11-10 ORDER -31.89 -77.46 0.00 3 Jones 2008-11-15 PAYMENT 73.86 -77.46 0.00 6 Marsch 2008-11-20 ORDER -14.37 -77.46 0.00 6 Marsch 2008-12-01 PAYMENT 14.37 -77.46 0.00 12 Meier 2008-11-17 ORDER -22.27 -77.46 -22.27 5 Peters 2008-11-05 ORDER -8.97 -77.46 -5.79 5 Peters 2008-11-15 ORDER -5.79 -77.46 -5.79 5 Peters 2008-11-20 PAYMENT 8.97 -77.46 -5.79 9 White 2008-11-17 ORDER -5.79 -77.46 -5.79 10 Young 2008-10-12 ORDER -20.50 -77.46 0.00 10 Young 2008-10-20 PAYMENT 20.50 -77.46 0.00 38 rows in resultset.
Beispiel 3: Mit PARTITION BY und ORDER BY Klausel
-
SELECT customer_no , last_name , transaction_date 'DATE' , transaction_type TYPE , transaction_sum 'SUM' -- calculates the balance of all customers , SUM(transaction_sum) OVER() total -- calculates the balance of each customer , SUM(transaction_sum) OVER(PARTITION BY customer_no) cust_balance -- calculates the running balance of each customer , SUM(transaction_sum) OVER(PARTITION BY customer_no ORDER BY transaction_date) running_balance FROM v_transactions ORDER BY last_name, transaction_date;
In diesem Beispiel wird mit Hilfe der ORDER BY Klausel für jeden Kunden ein laufender Kontostand berechnet.
-
CUS LAST_NAME DATE TYPE SUM TOTAL CUST_BAL RUNNING_ --- ------------ ---------- ------- -------- -------- -------- -------- 2 Adams 2008-10-11 ORDER -31.32 -77.46 0.00 -31.32 2 Adams 2008-10-25 ORDER -31.89 -77.46 0.00 -63.21 2 Adams 2008-10-25 RETURN 12.64 -77.46 0.00 -50.57 2 Adams 2008-10-27 PAYMENT 6.04 -77.46 0.00 -44.53 2 Adams 2008-11-02 ORDER -8.97 -77.46 0.00 -53.50 2 Adams 2008-11-03 ORDER -19.25 -77.46 0.00 -72.75 2 Adams 2008-11-04 PAYMENT 31.89 -77.46 0.00 -40.86 2 Adams 2008-11-10 RETURN 8.97 -77.46 0.00 -31.89 2 Adams 2008-11-25 ORDER -11.83 -77.46 0.00 -43.72 2 Adams 2008-11-30 PAYMENT 43.72 -77.46 0.00 0.00 7 Black 2008-10-20 ORDER -21.17 -77.46 0.00 -21.17 7 Black 2008-11-04 PAYMENT 21.17 -77.46 0.00 0.00 7 Black 2008-11-12 ORDER -17.03 -77.46 0.00 -17.03 7 Black 2008-11-17 PAYMENT 17.03 -77.46 0.00 0.00 4 Brown 2008-10-19 ORDER -27.49 -77.46 0.00 -27.49 4 Brown 2008-10-25 ORDER -15.13 -77.46 0.00 -42.62 4 Brown 2008-11-03 PAYMENT 27.49 -77.46 0.00 -15.13 4 Brown 2008-11-21 ORDER -21.04 -77.46 0.00 -36.17 4 Brown 2008-11-30 PAYMENT 36.17 -77.46 0.00 0.00 8 Green 2008-10-14 ORDER -16.48 -77.46 -43.61 -16.48 8 Green 2008-11-03 RETURN 8.24 -77.46 -43.61 -8.24 8 Green 2008-11-03 PAYMENT 8.24 -77.46 -43.61 0.00 8 Green 2008-11-09 ORDER -43.61 -77.46 -43.61 -43.61 3 Jones 2008-10-11 ORDER -42.02 -77.46 0.00 -42.02 3 Jones 2008-10-25 ORDER -34.38 -77.46 0.00 -76.40 3 Jones 2008-11-03 PAYMENT 26.84 -77.46 0.00 -49.56 3 Jones 2008-11-03 RETURN 7.59 -77.46 0.00 -41.97 3 Jones 2008-11-10 ORDER -31.89 -77.46 0.00 -73.86 3 Jones 2008-11-15 PAYMENT 73.86 -77.46 0.00 0.00 6 Marsch 2008-11-20 ORDER -14.37 -77.46 0.00 -14.37 6 Marsch 2008-12-01 PAYMENT 14.37 -77.46 0.00 0.00 12 Meier 2008-11-17 ORDER -22.27 -77.46 -22.27 -22.27 5 Peters 2008-11-05 ORDER -8.97 -77.46 -5.79 -8.97 5 Peters 2008-11-15 ORDER -5.79 -77.46 -5.79 -14.76 5 Peters 2008-11-20 PAYMENT 8.97 -77.46 -5.79 -5.79 9 White 2008-11-17 ORDER -5.79 -77.46 -5.79 -5.79 10 Young 2008-10-12 ORDER -20.50 -77.46 0.00 -20.50 10 Young 2008-10-20 PAYMENT 20.50 -77.46 0.00 0.00 38 rows in resultset.
Analytische Funktionen
Folgende Funktionen können sowohl als analytische als auch als Aggregatfunktionen benutzt werden. Dabei sind alle analytischen Klauseln erlaubt, es ist allerdings keine erforderlich:
- AVG: Berechnung des arithmetischen Mittelwerts
- COUNT: Berechnung der Anzahl der Werte
- MIN: Ermittlung des kleinsten Wertes
- MAX: Ermittlung des größten Wertes
- SUM: Aufsummierung der Werte
DENSE_RANK, RANK und ROW_NUMBER
Diese Funktionen haben kein Argument, die ORDER BY Klausel ist erforderlich. Sie geben einen Rang oder eine Zeilennummer innerhalb der Partionen zurück, wobei die ORDER BY Klausel die Rangfolge oder Nummerierung bestimmt.
Bei gleichen Werten des sortierten Ausdrucks liefern sowohl RANK als auch DENSE_RANK einen gemeinsamen Rang, wobei RANK im Unterschied zu DENSE_RANK anschließend entsprechend viele Plätze überspringt. ROW_NUMBER liefert in jedem Fall eindeutige Zahlen, wobei die Sortierung gleicher Werte dann zufällig ist.
Beispiel 4: RANK, DENSE_RANK und ROW_NUMBER()
-
SELECT product_no , product_name , order_month , sold_items , ROW_NUMBER() OVER( PARTITION BY order_month ORDER BY NVL(sold_items, 0) DESC NULLS LAST) num , RANK() OVER( PARTITION BY order_month ORDER BY NVL(sold_items, 0) DESC NULLS LAST) rating , DENSE_RANK() OVER( PARTITION BY order_month ORDER BY NVL(sold_items, 0) DESC NULLS LAST) dense FROM ( SELECT product_no , product_name , TRUNC(order_date, 'MM') order_month , SUM(COUNT) sold_items , SUM(SUM) revenue FROM V_PRODUCT_ORDERING GROUP BY product_no, product_name, TRUNC(order_date, 'MM') HAVING SUM(COUNT) > 0 ) ;
-
PRO PRODUCT_NAME ORDER_MONT SOLD_ITE NUM RATING DENSE --- ----------------------------------- ---------- -------- ------- ------- ------- 8 The Love Dare 2008-10-01 4 1 1 1 5 The Tales of Beedle the Bard 2008-10-01 3 2 2 2 7 Barefoot Contessa Back to Basics 2008-10-01 3 3 2 2 1 The Shack 2008-10-01 2 4 4 3 2 Breaking Dawn 2008-10-01 2 5 4 3 3 The Eclipse 2008-10-01 2 6 4 3 10 The Snowball 2008-10-01 1 7 7 4 6 Twilight The Twilight 2008-10-01 1 8 7 4 9 Brisingr 2008-10-01 1 9 7 4 4 New Moon 2008-10-01 1 10 7 4 5 The Tales of Beedle the Bard 2008-11-01 4 1 1 1 17 Dreams from My Father 2008-11-01 4 2 1 1 10 The Snowball 2008-11-01 2 3 3 2 8 The Love Dare 2008-11-01 2 4 3 2 12 Outliers 2008-11-01 2 5 3 2 15 The Last Lecture 2008-11-01 1 6 6 3 16 A Mercy 2008-11-01 1 7 6 3 3 The Eclipse 2008-11-01 1 8 6 3 6 Twilight The Twilight 2008-11-01 1 9 6 3 13 American Lion 2008-11-01 1 10 6 3 4 New Moon 2008-11-01 1 11 6 3 21 rows in resultset.
LAG und LEAD
Diese Funktionen erlauben den Zugriff auf Werte anderer Zeilen. ORDER BY ist zwingend erforderlich, PARTITION BY nicht.
Das Offset gibt an, welche Zeile der Sortierung entsprechend betrachtet wird, das Offset muss größer oder gleich 0 sein. Offset=0 ist die aktuelle Zeile. Offset kann sich auch aus einem Ausdruck heraus berechnen. Die Voreinstellung für das Offset ist 1.
Der Defaultwert wird benutzt, wenn keine Zeile mit dem entsprechenden Offset gefunden wird, weil z.B. die aktuelle Zeile die erste gemäß Sortierung ist. Auch der Defaultwert darf ein beliebiger Ausdruck sein, wird er nicht angegeben ist 'NULL' voreingestellt.
Syntax:
-
- LEAD/LAG(<expr>[, <offset>[, <default>]]) OVER([PARTITION BY <expr>] ORDER BY <expr>)
Beispiel 5: LAG
-
- WITH basis AS (
- SELECT
- customer_no, order_no, order_date,
- LAG(order_date) OVER(
- PARTITION BY customer_no
- ORDER BY order_date
- ) AS prev_date
- FROM orders
- )
- SELECT
- customer_no,
- COUNT(*) order_count,
- AVG(DAYS_BETWEEN(order_date, prev_date)) AS avg_days
- FROM basis
- GROUP BY customer_no
- ORDER BY customer_no;
LAG(order_date) wird im Subselect 'basis' verwendet, um zu jeder Bestellung zusätzlich das Datum der letzten Bestellung zu erhalten. Gemäß der gegebenen Partitionierungs- und Sortierungsregeln wird der Spaltenwert der vorherigen Zeile (offset=1) eingefügt. Gibt es keine vorherige Zeile, wird NULL (=default) eingesetzt.
Mit den beiden Bestelldaten kann man nun berechnen, wie lange (Anzahl Tage) der Kunde zwischenzeitlich nichts bestellt hat und diesen Wert aufaggregieren. Dieser zweite Schritt ist notwendig, da analytische Funktionen in der Select-Liste nicht in Ausdrücken verknüpft werden können.
-
- CUSTOMER_NO ORDER_COUNT AVG_DAYS
- ----------- ----------- ---------
- 2 5 11.25
- 3 3 15
- 4 3 16.5
- 5 2 10
- 6 1
- 7 2 23
- 8 2 26
- 9 1
- 10 1
- 12 1















