次の例は、Window 関数として使用される SUM 関数を示したものです。クエリは、DepartmentID 別にデータを分割した結果セットを返し、在籍経験が長い人から順に従業員の給与の累積合計 (Sum_Salary) を算出します。結果セットには、カリフォルニア州、ユタ州、ニューヨーク州、またはアリゾナ州に住む従業員のみが含まれます。Sum_Salary のカラムは、従業員の給与の累積合計です。
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; |
次のテーブルは、クエリからの結果セットを示します。結果セットは、DepartmentID ごとに分割されています。
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 |
DepartmentID 100 の場合、カリフォルニア州、ユタ州、ニューヨーク州、またはアリゾナ州に住む従業員の給与の累積合計は 434,091.69 ドルで、部門 200 の従業員の累積合計は 250,200.00 ドルです。
SUM 関数の正確な構文の詳細については、SUM 関数 [集合]を参照してください。
2 つのウィンドウ (現在のローと直前のローのそれぞれに対するウィンドウ) を使用すると、隣接ローのデルタ (変化量) を計算できます。たとえば、次のクエリの結果では、ある従業員とその直前の従業員の給与のデルタ (Delta) を計算します。
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 |
CurrentRow ウィンドウでは、ウィンドウのサイズが ROWS BETWEEN CURRENT ROW AND CURRENT ROW
に設定されているため、SUM は現在のローのみに対して実行されます。同様に、PreviousRow ウィンドウでは、ウィンドウのサイズが ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING
に設定されているため、SUM は直前のローのみに対して実行されます。最初のローには先行するローがないため、このローの PreviousRow の値は NULL です。そのため Delta の値も NULL になります。
次のクエリを考えてみます。このクエリは、データベース内で製品ごとに最上位の営業担当者 (総売り上げで定義) をリストします。
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; |
このクエリは、次の結果を返します。
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 |
元のクエリは、ProductID をサブクエリの相関外部参照として、ある製品の最高売り上げを判断する相関サブクエリを使用して作成されています。ただし、この場合のようにネストされたクエリを使用すると、コストの高いオプションになることがあります。これは、サブクエリに GROUP BY 句だけでなく、GROUP BY 句内の ORDER BY 句も含まれるからです。そのため、クエリ・オプティマイザは、同じセマンティックを保持したままになり、このネストされたクエリをジョインとして書き換えることができません。したがって、クエリの実行中は、外部ブロック内で計算される派生ローごとにサブクエリが評価されます。
グラフィカル・プランでコストの高い Filter 述部に注意してください。オプティマイザは、クエリの実行コストの 99% がこのプラン演算子に起因するものであると推定します。サブクエリのプランでは、メイン・ブロックのフィルタ演算子のコストが高くなる理由を明確にしています。サブクエリには、ネスト・ループ・ジョインが 2 つと、ハッシュ GROUP BY 演算が 1 つ、ソートが 1 つ含まれます。
ランキング関数を使用した同じクエリの書き換えでは、同じ結果が非常に効率よく計算されます。
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; |
この書き換えられたクエリは、プランがより単純になります。
GROUP BY 句が処理されてから select リスト項目の評価とクエリの ORDER BY 句の処理が行われるまでに、ウィンドウ演算子が計算されます。グラフィカルなプランでわかるように、3 つのテーブルのジョイン後、ジョインされたローは SalesRepresentative 属性と ProductID 属性の組み合わせでグループ化されます。したがって、total_quantity と total_sales の SUM 集合関数は、SalesRepresentative と ProductID の組み合わせごとに計算できます。
GROUP BY 句の評価に続いて、中間結果のローを total_sales の降順にランク付けするために、ウィンドウを使用して RANK 関数が計算されます。WINDOW 指定には PARTITION BY 句が含まれます。それによって、GROUP BY 句の結果が、今度は ProductID ごとに再分割 (再グループ化) されます。そのため、RANK 関数は、総売り上げの降順で製品ごとにローをランク付けしますが、その製品を販売した営業担当者はすべてまとめられます。このランキングにより、最上位の営業担当者を特定するのに必要なのは、ランクが 1 ではない派生テーブルのローを拒否するように派生テーブルの結果を制限するだけです。同順の場合 (結果セットのロー 7 と 8)、RANK は同じ値を返します。したがって、690 と 949 の両方の営業担当者が最終結果に出現します。
Copyright © 2009, iAnywhere Solutions, Inc. - SQL Anywhere 11.0.1 |