Window関数を使ったクエリ例
部署の平均給与とそれぞれの従業員の給与を使った例
サンプルのテーブル
depname | empno | salary | avg
----------|-------|--------|-----------------------
develop | 11 | 5200 | 5020.0000000000000000
develop | 7 | 4200 | 5020.0000000000000000
develop | 9 | 4500 | 5020.0000000000000000
develop | 8 | 6000 | 5020.0000000000000000
develop | 10 | 5200 | 5020.0000000000000000
personnel | 5 | 3500 | 3700.0000000000000000
personnel | 2 | 3900 | 3700.0000000000000000
sales | 3 | 4800 | 4866.6666666666666667
sales | 1 | 5000 | 4866.6666666666666667
sales | 4 | 4800 | 4866.6666666666666667
rank関数の例
rank()
関数は、それぞれの明確な ORDER BY
の値に対する現在行のパーティション内における順位を、 ORDER BY
句で定義された順序で生成する
depname
毎に salary
の高い順にランクを付ける場合のSQLクエリ例
SELECT depname, empno, salary,
rank() OVER (PARTITION BY depname ORDER BY salary DESC)
FROM empsalary;
depname | empno | salary | rank
----------|-------|--------|------
develop | 8 | 6000 | 1
develop | 10 | 5200 | 2
develop | 11 | 5200 | 2
develop | 9 | 4500 | 4
develop | 7 | 4200 | 5
personnel | 2 | 3900 | 1
personnel | 5 | 3500 | 2
sales | 1 | 5000 | 1
sales | 4 | 4800 | 2
sales | 3 | 4800 | 2
連番付与
row_number() OVER ()
を使って、連番付与のSQLクエリ例
- ソートした後に番号をふるのがポイント
PARTITION BY
が無い場合は、テーブル全体が対象となる
SELECT row_number() OVER (), *
FROM (SELECT * FROM tbl ORDER BY sortkey) AS t;
全体に対する割合で取得
- 上位10%を取得する場合ののSQLクエリ例
- 全体に対する割合を計算するには、結局は全体を取得する必要があることに注意
SELECT ...
FROM (SELECT *, cume_dist() OVER (ORDER BY i) AS rank FROM tbl) AS t
WHERE rank <= 0.1;
PARTITION BY
と ORDER BY
の組み合わせ
PARTITION BY
と ORDER BY
は、同時に使用することも可能
- 地域ごとに身長の低い順に番号付けする場合のSQLクエリ例
SELECT id, 地域, 身長,
rank() OVER (PARTITION BY 地域 ORDER BY 身長)
FROM tbl;
歯抜けIDの検索
- 列
id
でソートした際に1つ前の行と比較して、連番になっていない行を抜き出す場合のSQLクエリ例
- 前の行を取得する
lag()
関数を使うと、ただ1回のテーブルスキャンで処理することが可能
NOT IN
や NOT EXISTS
などコストの高いSQLを使う必要がなくなる
SELECT id, prev_id
FROM (
SELECT id,
lag(id, 1, 0) OVER (ORDER BY id) AS prev_id
FROM tbl
) AS t
WHERE id <> prev_id + 1;
Window演算が行われた後、行にフィルタ処理を行ったりグループ化を行う場合
- Window演算が行われた後、行にフィルタ処理を行ったりグループ化を行う必要が生じた場合、副問い合わせを使用する
3
より小さい rank
を持った内部問い合わせからの行のみを表示するSQLクエリ例
SELECT depname, empno, salary, enroll_date
FROM (
SELECT depname, empno, salary, enroll_date,
rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos
FROM empsalary
) AS ss
WHERE pos < 3;
複数の関数で同じWindow処理動作が必要な場合
WINDOW
句でWindow処理動作に名前を付け、これを OVER
内で参照することが可能
SELECT sum(salary) OVER w, avg(salary) OVER w
FROM empsalary
WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);