CodeGym /コース /SQL SELF /ウィンドウ関数のよくあるミス

ウィンドウ関数のよくあるミス

SQL SELF
レベル 30, レッスン 4
使用可能

そろそろウィンドウ関数を使うときにハマりがちなポイントについて話そうと思う。プログラミング(てか人生もだけど)では、他人のミスから学ぶのが一番効率いいよね。ここでは初心者がやりがちなミス、時にはベテランでもやっちゃうミスを取り上げて、どうやって避けるか一緒に見ていこう。

ミスその1: PARTITION BYの使い方ミス

一番ありがちなのが、PARTITION BYを忘れたり、間違った指定をしちゃうこと。特にデータをグループごとに分けたいときにやりがち。これがないと、PostgreSQLは全行を一つの大きなグループとして扱っちゃうから、思った通りの結果にならないことが多い。

例えば、salesっていう売上データのテーブルがあるとする:

id region month total
1 North 2023-01 1000
2 South 2023-01 800
3 North 2023-02 1200
4 South 2023-02 900

各地域ごとに月ごとの累積売上(SUM())を出したいとき、こんなクエリを書きがち:

SELECT
    region,
    month,
    SUM(total) OVER (ORDER BY month) AS running_total
FROM 
    sales;

結果:

region month running_total
North 2023-01 1000
South 2023-01 1800
North 2023-02 3000
South 2023-02 3900

パッと見は良さそうだけど、実は期待した結果じゃない。累積合計が地域ごとじゃなくて、全体で計算されてる。これはPARTITION BY regionを忘れてるせい。

修正版コード:

SELECT 
    region,
    month,
    SUM(total) OVER (PARTITION BY region ORDER BY month) AS running_total
FROM 
    sales;

結果:

region month running_total
North 2023-01 1000
North 2023-02 2200
South 2023-01 800
South 2023-02 1700

これでちゃんと地域ごとにグループ化されて、累積合計もそれぞれ計算されるようになった!

ミスその2: ORDER BYの順序ミス

OVER()の中のORDER BYは、ウィンドウ内での行の順番を決める。ここを間違えると、思わぬ結果になることがある。

例えば、月ごとの売上累積を降順で出したいとき、こんなクエリを書いたとする:

SELECT
    month,
    total,
    SUM(total) OVER (ORDER BY month DESC) AS running_total
FROM 
    sales;

結果:

month total running_total
2023-02 1200 1200
2023-02 900 2100
2023-01 1000 3100
2023-01 800 3900

一見正しそうだけど、よく見ると月ごとにグループ化されてるだけで、累積合計が変な感じになってる。これは降順にしたせいで、計算の順番が変わっちゃってるから。

修正方法: ORDER BYを正しい順序(昇順)に直そう:

SELECT
    month,
    total,
    SUM(total) OVER (ORDER BY month ASC) AS running_total
FROM 
    sales;

ミスその3: インデックスなしでウィンドウ関数を使う

ウィンドウ関数は大量データを扱うことが多いから、キーとなるカラムにインデックスがないと、パフォーマンスが激落ちすることもある。

例: large_salesっていう何百万行もあるテーブルで、売上のランクを計算したいとする:

SELECT
    id,
    total,
    RANK() OVER (ORDER BY total DESC) AS rank
FROM 
    large_sales;

データが少なければすぐ終わるけど、量が多いとめっちゃ時間かかる。

修正方法:ORDER BYで使うカラムにインデックスを貼ろう:

CREATE INDEX idx_total ON large_sales(total DESC);

これでクエリの実行がかなり速くなるよ。

ミスその4: ROWSやRANGEの意味を勘違いする

ROWSやRANGEを使うときは、それぞれがどうやってウィンドウを決めるかちゃんと理解しておくのが大事。ここを勘違いすると、思わぬ結果になる。

例: 今月とその前2ヶ月の移動平均を出したいとき:

SELECT
    month,
    AVG(total) OVER (ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM 
    sales;

もしROWSの代わりにRANGEを使うと:

SELECT
    month,
    AVG(total) OVER (ORDER BY month RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM 
    sales;

結果が変わる。なぜならRANGEは値の範囲でウィンドウを決めるけど、ROWSは行数で決めるから。

ミスその5: ウィンドウ関数の使いすぎ

1つのクエリでウィンドウ関数を何回も使うと、計算が重複してパフォーマンスが落ちることがある。

例:

SELECT 
    id,
    total,
    SUM(total) OVER (PARTITION BY region) AS region_total,
    SUM(total) OVER (PARTITION BY region) / COUNT(total) OVER (PARTITION BY region) AS region_avg
FROM 
    sales;

ここではSUM(total)とCOUNT(total)が各行ごとに何度も計算されてる。

修正方法: サブクエリやCTEを使ってクエリをシンプルにしよう:

WITH cte_region_totals AS (
    SELECT 
        region,
        SUM(total) AS region_total,
        COUNT(total) AS region_count
    FROM 
        sales
    GROUP BY 
        region
)
SELECT 
    s.id,
    s.total,
    t.region_total,
    t.region_total / t.region_count AS region_avg
FROM 
    sales s
JOIN 
    cte_region_totals t ON s.region = t.region;

ミスを避けるコツ

PARTITION BYとORDER BYを必ずチェック:ウィンドウの指定が正しいか毎回確認しよう。

インデックスを貼る:特にORDER BYやフィルタを使う場合は必須。

複数回の計算にはCTEを使う:重複計算を減らせるよ。

実行プランを確認する:EXPLAINやEXPLAIN ANALYZEでPostgreSQLがどうクエリを処理してるか見てみよう。

本番データでテストする:結果が期待通りか、ちゃんと目的が達成できてるか確認しよう。

2
タスク
SQL SELF, レベル 30, レッスン 4
ロック未解除
ORDER BYのウィンドウ関数での使用
ORDER BYのウィンドウ関数での使用
1
アンケート/クイズ
ウィンドウフレームのカスタマイズ、レベル 30、レッスン 4
使用不可
ウィンドウフレームのカスタマイズ
ウィンドウフレームのカスタマイズ
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION