ウィンドウ関数入門 — 行をまたいで計算する
SQLのウィンドウ関数(ROW_NUMBER・RANK・SUMのOVER句)を入門から解説します。GROUP BYとの違い、PARTITION BYやORDER BYの使い方を、順位付けや累計の例で整理します。
ウィンドウ関数は、SQL の中でも一段レベルの高い、しかし非常に強力な機能です。「部門ごとの給料順位」「日ごとの売上の累計」のように、行を集約せずに、他の行を参照した計算ができます。集計(GROUP BY)との違いを理解するのが第一歩です。
GROUP BY との違い
GROUP BY は行をまとめてしまうため、集計すると個々の行は消えます。たとえば GROUP BY dept で平均給料を出すと、結果は部門ごとに 1 行になり、社員名は出せません。
ウィンドウ関数は違います。元の行を残したまま、その行に関連する集計値を横に付け足せます。
SELECT
name,
dept,
salary,
AVG(salary) OVER (PARTITION BY dept) AS 部門平均
FROM employees;このクエリは、社員 1 人 1 行を保ったまま、各行に「その人の所属部門の平均給料」を付けます。OVER (...) が付くことで、その関数は集約ではなくウィンドウ関数として振る舞います。
OVER 句の中身
ウィンドウ関数の要は OVER (...) です。中には主に 2 つを書けます。
PARTITION BY 列:計算を行うグループを指定する(GROUP BY に相当するが、行はまとめない)ORDER BY 列:グループ内の順序を指定する(順位や累計で使う)
PARTITION BY を省略すると、全行が 1 つのグループとして扱われます。
順位を付ける(ROW_NUMBER / RANK)
代表的なウィンドウ関数が、順位を付けるものです。
SELECT
name,
dept,
salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS 部門内順位
FROM employees;PARTITION BY dept で部門ごとに分け、ORDER BY salary DESC で給料の高い順に並べ、その順番に番号を振ります。似た関数に違いがあります。
| 関数 | 同点のときの振る舞い |
|---|---|
ROW_NUMBER() | 必ず連番(1,2,3…)。同点でも別の番号 |
RANK() | 同点は同じ順位。次はスキップ(1,1,3…) |
DENSE_RANK() | 同点は同じ順位。次はスキップしない(1,1,2…) |
「各部門の給料 1 位」を出したいときは、ROW_NUMBER() で 1 位を絞る、といった使い方をします。
累計を出す
ORDER BY と組み合わせると、累計(running total)が計算できます。
SELECT
sale_date,
amount,
SUM(amount) OVER (ORDER BY sale_date) AS 累計売上
FROM sales;日付順に、その日までの売上を足し上げていきます。SUM(...) OVER (ORDER BY ...) は「先頭からその行までの合計」を返すため、時系列の累計や進捗の可視化に便利です。
いつ使うか
ウィンドウ関数が向いているのは、次のような場面です。
- 順位付け:グループごとのトップ N、ランキング
- 累計・移動平均:時系列データの積み上げ
- 前後の行との比較:
LAG/LEAD(前後の行の値を参照)
同じことをサブクエリや自己結合でも書けますが、ウィンドウ関数のほうが簡潔で読みやすくなることが多いです。
まとめ
- ウィンドウ関数は行を集約せず、行をまたいだ計算を行う(元の行が残る)
OVER (PARTITION BY ... ORDER BY ...)でグループと順序を指定するROW_NUMBER/RANK/DENSE_RANKで順位を付ける(同点の扱いが違う)SUM(...) OVER (ORDER BY ...)で累計が出せる
ウィンドウ関数は最初はとっつきにくいですが、使えるようになると分析系のクエリが一気に書きやすくなります。まずは「行が消えない集計」というイメージから入るとよいでしょう。