Im folgenden Beispiel wird die SUM-Funktion als eine Fensterfunktion verwendet. Die Abfrage gibt eine Ergebnismenge zurück, die die Daten nach "DepartmentID" partitioniert und dann eine kumulative Summe der Gehälter beginnend mit dem am längsten im Unternehmen beschäftigten Mitarbeiter anzeigt. Die Ergebnismenge enthält nur die Mitarbeiter, die in Kalifornien, Utah, New York oder Arizona leben. Die Spalte "Sum_Salary" liefert die kumulative Gesamtsumme der Mitarbeitergehälter.
SELECT DepartmentID, Surname, StartDate, Salary, SUM( Salary ) OVER ( PARTITION BY DepartmentID ORDER BY StartDate RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS "Sum_Salary" FROM Employees WHERE State IN ( 'CA', 'UT', 'NY', 'AZ' ) AND DepartmentID IN ( '100', '200' ) ORDER BY DepartmentID, StartDate; |
Die nachstehende Tabelle stellt die Ergebnismenge dieser Abfrage dar. Die Ergebnismenge wird nach "DepartmentID" partitioniert.
DepartmentID | Surname | StartDate | Salary | Sum_Salary | |
---|---|---|---|---|---|
1 | 100 | Whitney | 1984-08-28 | 45700.00 | 45700.00 |
2 | 100 | Cobb | 1985-01-01 | 62000.00 | 107700.00 |
3 | 100 | Shishov | 1986-06-07 | 72995.00 | 180695.00 |
4 | 100 | Driscoll | 1986-07-01 | 48023.69 | 228718.69 |
5 | 100 | Guevara | 1986-10-14 | 42998.00 | 271716.69 |
6 | 100 | Wang | 1988-09-29 | 68400.00 | 340116.69 |
7 | 100 | Soo | 1990-07-31 | 39075.00 | 379191.69 |
8 | 100 | Diaz | 1990-08-19 | 54900.00 | 434091.69 |
9 | 200 | Overbey | 1987-02-19 | 39300.00 | 39300.00 |
10 | 200 | Martel | 1989-10-16 | 55700.00 | 95000.00 |
11 | 200 | Savarino | 1989-11-07 | 72300.00 | 167300.00 |
12 | 200 | Clark | 1990-07-21 | 45000.00 | 212300.00 |
13 | 200 | Goggin | 1990-08-05 | 37900.00 | 250200.00 |
Für die DepartmentID 100 ist die kumulative Gesamtsumme der Gehälter von Mitarbeitern in Kalifornien, Utah, New York und Arizona insgesamt $434,091.69 und die kumulative Gesamtsumme für die Mitarbeiter in DepartmentID 200 beträgt $250,200.00.
Weitere Hinweise zur genauen Syntax der SUM-Funktion finden Sie unter SUM-Funktion [Aggregat].
Durch die Verwendung von zwei Fenstern - ein Fenster für die aktuelle Zeile, das andere für die nächste Zeile - können Sie Deltawerte bzw. Unterschiede zwischen zwei angrenzenden Zeilen berechnen. Die folgende Abfrage berechnet beispielsweise den Deltawert (Delta) zwischen den Gehältern eines Mitarbeiters und des nächsten Mitarbeiters in den Ergebnissen:
SELECT EmployeeID AS EmployeeNumber, Surname AS LastName, SUM( Salary ) OVER ( ORDER BY BirthDate ROWS BETWEEN CURRENT ROW AND CURRENT ROW ) AS CurrentRow, SUM( Salary ) OVER ( ORDER BY BirthDate ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING ) AS PreviousRow, ( CurrentRow - PreviousRow ) AS Delta FROM Employees WHERE State IN ( 'NY' ); |
EmployeeNumber | LastName | CurrentRow | PreviousRow | Delta | |
---|---|---|---|---|---|
1 | 913 | Martel | 55700.000 | (NULL) | (NULL) |
2 | 1062 | Blaikie | 54900.000 | 55700.000 | -800.000 |
3 | 249 | Guevara | 42998.000 | 54900.000 | -11902.000 |
4 | 390 | Davidson | 57090.000 | 42998.000 | 14092.000 |
5 | 102 | Whitney | 45700.000 | 57090.000 | -11390.000 |
6 | 1507 | Wetherby | 35745.000 | 45700.000 | -9955.000 |
7 | 1751 | Ahmed | 34992.000 | 35745.000 | -753.000 |
8 | 1157 | Soo | 39075.000 | 34992.000 | 4083.000 |
Beachten Sie, dass SUM nur auf die aktuelle Zeile für das CurrentRow-Fenster angewendet wird, da die Fenstergröße auf ROWS BETWEEN CURRENT ROW AND CURRENT ROW
eingestellt wurde. Ebenso wird SUM nur auf die vorherige Zeile für das PreviousRow-Fenster angewendet, da die Fenstergröße
auf ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING
eingestellt wurde. Der Wert von PreviousRow in der ersten Zeile ist NULL, da es keine vorherige Zeile gibt und somit auch
der Deltawert NULL beträgt.
In der folgenden Abfrage werden die Spitzenverkäufer (definiert nach Gesamtumsatz) für die einzelnen Produkte in der Datenbank aufgeführt:
SELECT s.ProductID AS Products, o.SalesRepresentative, SUM( s.Quantity ) AS total_quantity, SUM( s.Quantity * p.UnitPrice ) AS total_sales FROM SalesOrders o KEY JOIN SalesOrderItems s KEY JOIN Products p GROUP BY s.ProductID, o.SalesRepresentative HAVING total_sales = ( SELECT First SUM( s2.Quantity * p2.UnitPrice ) AS sum_sales FROM SalesOrders o2 KEY JOIN SalesOrderItems s2 KEY JOIN Products p2 WHERE s2.ProductID = s.ProductID GROUP BY o2.SalesRepresentative ORDER BY sum_sales DESC ) ORDER BY s.ProductID; |
Diese Abfrage liefert das folgende Ergebnis:
Products | SalesRepresentative | total_quantity | total_sales | |
---|---|---|---|---|
1 | 300 | 299 | 660 | 5940.00 |
2 | 301 | 299 | 516 | 7224.00 |
3 | 302 | 299 | 336 | 4704.00 |
4 | 400 | 299 | 458 | 4122.00 |
5 | 401 | 902 | 360 | 3600.00 |
6 | 500 | 949 | 360 | 2520.00 |
7 | 501 | 690 | 360 | 2520.00 |
8 | 501 | 949 | 360 | 2520.00 |
9 | 600 | 299 | 612 | 14688.00 |
10 | 601 | 299 | 636 | 15264.00 |
11 | 700 | 299 | 1008 | 15120.00 |
Die ursprüngliche Abfrage wird durch eine korrelierte Unterabfrage gebildet, welche die höchsten Verkäufe für jedes einzelne Produkt ermittelt, da "ProductID" die korrelierte äußere Referenz der Unterabfrage ist. Wie auch in diesem Fall ist eine verschachtelte Abfrage jedoch oft eine kostenträchtige Option. Dies liegt daran, dass die Unterabfrage nicht nur eine GROUP BY-Klausel umfasst, sondern innerhalb dieser auch eine ORDER BY-Klausel. Dies macht es dem Abfrageoptimierer unmöglich, die verschachtelte Abfrage zu einem Join umzuschreiben und dabei die gleiche Semantik beizubehalten. Daher wird die Unterabfrage während der Abfrageausführung für jede abgeleitete Zeile ausgewertet, die im äußeren Block berechnet wird.
Beachten Sie das kostenträchtige Filterprädikat im grafischen Plan: Der Optimierer schätzt, dass 99 % der Ausführungskosten der Abfrage auf diesen Plan-Operator zurückzuführen sind. Der Plan für die Unterabfrage zeigt deutlich, warum der Filter-Operator im Hauptblock so kostenträchtig ist: Die Unterabfrage enthält zwei Nested Loops Joins, einen Hash-basierten GROUP BY-Vorgang und einen Sortiervorgang.
Ein Umschreiben der gleichen Abfrage mithilfe einer Rangfunktion berechnet das identische Ergebnis viel effizienter:
SELECT v.ProductID, v.SalesRepresentative, v.total_quantity, v.total_sales FROM ( SELECT o.SalesRepresentative, s.ProductID, SUM( s.Quantity ) AS total_quantity, SUM( s.Quantity * p.UnitPrice ) AS total_sales, RANK() OVER ( PARTITION BY s.ProductID ORDER BY SUM( s.Quantity * p.UnitPrice ) DESC ) AS sales_ranking FROM SalesOrders o KEY JOIN SalesOrderItems s KEY JOIN Products p GROUP BY o.SalesRepresentative, s.ProductID ) AS v WHERE sales_ranking = 1 ORDER BY v.ProductID; |
Die umgeschriebene Abfrage ergibt einen einfacheren Plan:
Bedenken Sie, dass ein Fenster-Operator nach der Verarbeitung einer GROUP BY-Klausel und vor der Auswertung der Elemente der SELECT-Liste sowie der ORDER BY-Klausel der Abfrage berechnet wird. Wie im grafischen Plan zu erkennen ist, werden die verknüpften Zeilen nach der Verknüpfung der drei Tabellen durch die Kombination der Attribute "SalesRepresentative" und "ProductID" gruppiert. Daher können die SUM-Aggregatfunktionen "total_quantity" und "total_sales" für alle Kombinationen von "SalesRepresentative" und "ProductID" berechnet werden.
Nach der Auswertung der GROUP BY-Klausel wird die RANK-Funktion berechnet, um mithilfe eines Fensters die Rangfolge der Zeilen im Zwischenergebnis absteigend nach "total_sales" darzustellen. Beachten Sie, dass die WINDOW-Spezifikation eine PARTITION BY-Klausel umfasst. Dadurch wird das Ergebnis der GROUP BY-Klausel neu partitioniert (oder neu gruppiert) - dieses Mal nach "ProductID". Deshalb sortiert die RANK-Funktion die Zeilen für jedes Produkt absteigend nach "total_sales", jedoch nur für die Verkäufer, die dieses Produkt verkauft haben. Durch diese Rangfolge ist es zum Ermitteln der Spitzenverkäufer lediglich erforderlich, das Ergebnis der abgeleiteten Tabelle einzuschränken, um die Zeilen auszuschließen, in denen die Rangfolge nicht 1 ist. Bei gleichen Werten (Zeilen 7 und 8 in der Ergebnismenge), liefert RANK den gleichen Wert. Daher erscheinen die Verkäufer 690 und 949 beide im Endergebnis.
Kommentieren Sie diese Seite in DocCommentXchange. Senden Sie uns Feedback über diese Seite via E-Mail. |
Copyright © 2009, iAnywhere Solutions, Inc. - SQL Anywhere 11.0.1 |