はじめに
Window関数とは、ウィンドウと呼ばれる範囲のレコードに対して集計や演算を行う関数です。
使用データ
emplyeeテーブル
| id | name | department | salary | hire_date |
| 1 | 田中 太郎 | 営業部 | 450000.0 | 2018-04-01 |
| 2 | 鈴木 花子 | 営業部 | 520000.0 | 2016-04-01 |
| 3 | 佐藤 次郎 | 営業部 | 380000.0 | 2020-04-01 |
| 4 | 高橋 美咲 | 開発部 | 600000.0 | 2015-04-01 |
| 5 | 伊藤 健一 | 開発部 | 550000.0 | 2017-04-01 |
| 6 | 渡辺 由美 | 開発部 | 480000.0 | 2019-04-01 |
| 7 | 山本 大輔 | 人事部 | 500000.0 | 2014-04-01 |
| 8 | 中村 さくら | 人事部 | 420000.0 | 2021-04-01 |
daily_salesテーブル
| date | sales |
| 2025-05-01 | 120000.0 |
| 2025-05-02 | 135000.0 |
| 2025-05-03 | 98000.0 |
| 2025-05-04 | 142000.0 |
| 2025-05-05 | 158000.0 |
| 2025-05-06 | 110000.0 |
| 2025-05-07 | 125000.0 |
GROUP BYとの相違点
Window関数と同じようにレコードをグループ化するGROUP BY関数とどこが異なるのでしょうか?
ここではemployeesテーブルにdepartmentごとに平均賃金を追加したい時を考えてみます。
GROUP BY関数を使用する場合だと、複数の行を一つにまとめて後でemployeeテーブルと結合しなくていけません。
| department | AVG(salary) |
| 営業部 | 450000.0 |
| 開発部 | 543333.333333 |
| 人事部 | 460000.0 |
一方、Window関数は元のレコードを残したまま、各レコードに対して集計結果を付与します。
| name | department | salary | dept_avg |
| 山本 大輔 | 人事部 | 500000.0 | 460000.0 |
| 中村 さくら | 人事部 | 420000.0 | 460000.0 |
| 田中 太郎 | 営業部 | 450000.0 | 450000.0 |
| 鈴木 花子 | 営業部 | 520000.0 | 450000.0 |
| 佐藤 次郎 | 営業部 | 380000.0 | 450000.0 |
| 高橋 美咲 | 開発部 | 600000.0 | 543333.333333 |
| 伊藤 健一 | 開発部 | 550000.0 | 543333.333333 |
| 渡辺 由美 | 開発部 | 480000.0 | 543333.333333 |
この時使用しているクエリ
SELECT name, department, salary,
AVG(salary) OVER (PARTITION BY department) AS dept_avg
FROM employees;
のAVG(salary) OVER (PARTITION BY department)がWindow関数の部分でPARTITION BYがグループ分けする基準(部門ごと、顧客ごとなど)を示します。
ランキング系の関数
ウィンドウの中での順位知りたい場合に使用します。
主な関数として
ROW_NUMBER()— 連番(1, 2, 3, 4…)RANK()— 同順位あり、次は飛ぶ(1, 2, 2, 4…)DENSE_RANK()— 同順位あり、次は飛ばない(1, 2, 2, 3…
実用例
部門内の給与ランキング
SELECT name, department, salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank
FROM employees;
department基準でグループ分けした際(PARTITION BY department)、降順で順位づけします(RANK() …. ORDER BY salary DESC)
| name | department | salary | salary_rank |
| 山本 大輔 | 人事部 | 500000.0 | 1 |
| 中村 さくら | 人事部 | 420000.0 | 2 |
| 鈴木 花子 | 営業部 | 520000.0 | 1 |
| 田中 太郎 | 営業部 | 450000.0 | 2 |
| 佐藤 次郎 | 営業部 | 380000.0 | 3 |
| 高橋 美咲 | 開発部 | 600000.0 | 1 |
| 伊藤 健一 | 開発部 | 550000.0 | 2 |
| 渡辺 由美 | 開発部 | 480000.0 | 3 |
前後参照系の関数
ウィンドウの中での前後の比較をしたい場合の使用します。
主な関数として
LAG(列, n)— n行前の値LEAD(列, n)— n行後の値FIRST_VALUE(),LAST_VALUE()— 最初/最後の値
実用例
前日比売上
SELECT date, sales,
sales - LAG(sales, 1) OVER (ORDER BY date) AS diff_from_prev
FROM daily_sales;
日付順で並べたとき(ORDER BY date)、1つ前のレコードのsales値(LAG(sales, 1))。つまりsales - LAG(sales, 1) OVER (ORDER BY date) は昨日に比べてどのくらい売り上げが伸びているかを表す。
| date | sales | diff_from_prev |
| 2025-05-01 | 120000.0 | null |
| 2025-05-02 | 135000.0 | 15000.0 |
| 2025-05-03 | 98000.0 | -37000.0 |
| 2025-05-04 | 142000.0 | 44000.0 |
| 2025-05-05 | 158000.0 | 16000.0 |
| 2025-05-06 | 110000.0 | -48000.0 |
| 2025-05-07 | 125000.0 | 15000.0 |
集計系の関数
データの特徴を見たいときに使用する
主な関数としてSUM(), AVG(), COUNT(), MAX(), MIN()
実用例
移動平均(直近3日)
SELECT date, sales,
AVG(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM daily_sales;
日付順に並べて(ORDER BY date)、2行前から(ROWS BETWEEN 2 PRECEDING)、現在の行まで(CURRENT ROW)をウィンドウの対象として、その平均を計算します。
| date | sales | moving_avg |
| 2025-05-01 | 120000.0 | 120000.0 |
| 2025-05-02 | 135000.0 | 127500.0 |
| 2025-05-03 | 98000.0 | 117666.666667 |
| 2025-05-04 | 142000.0 | 125000.0 |
| 2025-05-05 | 158000.0 | 132666.666667 |
| 2025-05-06 | 110000.0 | 136666.666667 |
| 2025-05-07 | 125000.0 | 131000.0 |
まとめ
このようにWindow関数を使用できるようになれば長く複雑なクエリをかく必要がなくなったり、より直感的にクエリのかけるようになるので是非使いこなしてみてください。

