例えば、自分の会社の収入を追いかけたり、ネットショップの売上を見たり、あるいは単純に自分の1年の支出を分析したいとするよね。毎月の収入や支出を見るだけじゃなくて、月ごとにどれだけ積み上がってるかも知りたいはず。
普通の集約関数(GROUP BY)だと、データをグループ化してグループごとに1行しか返してくれない。でも、毎月のデータを全部見ながら累積合計を出したい場合はどうする?そこでSUM()とウィンドウ関数の出番!
累積合計のためのウィンドウ関数の基本
ウィンドウ関数を使うと、ウィンドウフレームごとに集約処理ができるんだ。つまり、各行ごとに値を足し合わせたりできるけど、他の行を消さずに済む。もうGROUP BYの犠牲になる必要なし!
SUM()とウィンドウ関数の基本構文
累積合計を出すためのベースとなるテンプレートはこれ:
SELECT
column_name,
SUM(column_name) OVER (PARTITION BY partition_column ORDER BY order_column) AS cumulative_sum
FROM
table_name;
ここで:
SUM(column_name)— 値を合計する。OVER()— ウィンドウを指定する。PARTITION BY— データをグループ分け(オプション)。ORDER BY— ウィンドウ内の行の順番を決める。
例:月ごとの累積収入
収入のテーブルがあるとしよう:
| 月 | 収入 |
|---|---|
| 2023-01 | 1000 |
| 2023-02 | 1500 |
| 2023-03 | 2000 |
毎月の収入と累積合計を見たい。SQLクエリを書いてみよう:
SELECT
月,
収入,
SUM(収入) OVER (ORDER BY 月) AS 累積_収入
FROM
収入テーブル;
結果:
| 月 | 収入 | 累積_収入 |
|---|---|---|
| 2023-01 | 1000 | 1000 |
| 2023-02 | 1500 | 2500 |
| 2023-03 | 2000 | 4500 |
何が起きてるかというと:
OVER()の中のORDER BY 月で、PostgreSQLに「行を時系列順で見てね」と伝えてる。- 各行ごとに、それまでの全ての行(+今の行)を合計してる。
ここ、よく考えてみて。1行目はSUM()で1行目だけ、2行目は2行分、3行目は3行分の合計。だから月の並び順が超大事!
例:地域ごとの累積収入
もし地域ごとの売上テーブルがあったら、こんな感じ:
| 地域 | 月 | 収入 |
|---|---|---|
| 北部 | 2023-01 | 1000 |
| 北部 | 2023-02 | 1500 |
| 南部 | 2023-01 | 2000 |
| 南部 | 2023-02 | 2500 |
今度は、地域ごとに累積収入を計算したい:
SELECT
地域,
月,
収入,
SUM(収入) OVER (PARTITION BY 地域 ORDER BY 月) AS 累積_収入
FROM
売上;
結果はこうなる:
| 地域 | 月 | 収入 | 累積_収入 |
|---|---|---|---|
| 北部 | 2023-01 | 1000 | 1000 |
| 北部 | 2023-02 | 1500 | 2500 |
| 南部 | 2023-01 | 2000 | 2000 |
| 南部 | 2023-02 | 2500 | 4500 |
今度は各地域ごとに分析されてる(PARTITION BY 地域)、でも地域内では時系列順(ORDER BY 月)。
移動平均(AVG())
累積合計も便利だけど、「直近3ヶ月のトレンドを見たい!」みたいな時は移動平均がピッタリ。
例:収入の移動平均
また収入テーブルを使うよ。データはこんな感じ:
| 月 | 収入 |
|---|---|
| 2023-01 | 1000 |
| 2023-02 | 1500 |
| 2023-03 | 2000 |
| 2023-04 | 2500 |
3ヶ月移動平均を出すクエリ:
SELECT
月,
収入,
AVG(収入) OVER (
ORDER BY 月
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS 移動平均
FROM
収入;
結果:
| 月 | 収入 | 移動平均 |
|---|---|---|
| 2023-01 | 1000 | 1000 |
| 2023-02 | 1500 | 1250 |
| 2023-03 | 2000 | 1500 |
| 2023-04 | 2500 | 2000 |
解説:
ROWS BETWEEN 2 PRECEDING AND CURRENT ROWは、PostgreSQLに「今の行と2つ前までを見て平均出してね」と伝えてる。- つまり、各月ごとに直近3ヶ月の平均収入が見える。
つまり、各行ごとに3行分(今+2つ前)をウィンドウにして平均を出す。めっちゃ便利!
ORDER BYの動きとその影響
ウィンドウ関数は、行の順番がちゃんとしてないとダメ。順番が変だったり、指定しなかったりすると、結果が予想外になることも。
例:ORDER BYなしのミス
OVER()からORDER BYを外すと、累積合計じゃなくて、全行の合計が毎行に出ちゃう:
SELECT
月,
収入,
SUM(収入) OVER () AS 間違った_累積合計
FROM
収入;
結果:
| 月 | 収入 | 間違った累積合計 |
|---|---|---|
| 2023-01 | 1000 | 7000 |
| 2023-02 | 1500 | 7000 |
| 2023-03 | 2000 | 7000 |
| 2023-04 | 2500 | 7000 |
行の順番がなくなって、累積合計じゃなくて、全行の合計が毎行に出てきちゃう。
リアルな使い方ケース
収入分析:
- 累積合計で、会社の売上や収入の伸びを追える。
- 移動平均で、ノイズのない「本当の」トレンドが見える。
ファイナンシャルモデリング:
銀行や金融系の会社は、ウィンドウ関数で支払い分析や債務の増加、他の指標を見てる。
時系列データの分析:
オンラインユーザー数、ページビュー、売上などの時系列データは、SUM()やAVG()で分析するのがピッタリ。
GO TO FULL VERSION