CodeGym /コース /SQL SELF /OVER()のシンタックスとその主な特徴

OVER()のシンタックスとその主な特徴

SQL SELF
レベル 29 , レッスン 2
使用可能

OVER()は、どの行セットにウィンドウ関数を適用するかをSQLに伝える命令だよ。 ざっくり言うと、ウィンドウ関数を使うための「ウィンドウ」(データの範囲)を決める方法。例えば、人がいっぱい入ってる部屋があって、床の1平方メートルごとに何人いるか数えたいとする。OVER()は、部屋のどの部分に注目するかを指定する感じ。他の言い方をすると、どの行セットで関数が動くかを決めるんだ。

OVER()オペレーターは、ウィンドウ関数と一緒に使う専用で、テーブルの1つまたは複数の行に対してグループ化せずに操作できる。

シンタックス:

ウィンドウ関数() OVER (
    [PARTITION BY ...]
    [ORDER BY ...]
    [ROWS/RANGE ...]
)

各パーツの意味:

  • PARTITION BY — データセットを論理的なグループに分ける
  • ORDER BY — 各グループ内での行の順番を決める
  • ROWS/RANGE — 「ウィンドウ」のサイズを指定(例:現在の行+次の1行)

例:パラメータなしのOVER()

OVER()を追加パラメータなしで使うと、その前に書いた関数が全データセットに対して動くって意味になる。

SELECT
    employee_id,
    salary,
    ROW_NUMBER() OVER () AS row_num -- ROW_NUMBER()は結果の全行に適用される
FROM employees;

何が起きてる?

  1. ROW_NUMBER()は各行にユニークな番号を振る。
  2. OVER()に何も指定しないと、employeesテーブルの全行が1つのまとまりとして扱われる。

結果:

employee_id salary row_num
1 50000 1
2 60000 2
3 55000 3

PARTITION BYでグループを指定する

OK、今度は会社全体じゃなくて、各部署ごとに社員をナンバリングしたいとしよう。ここでPARTITION BYの出番!

OVER()の中のPARTITION BYは、データをグループ(または「パーティション」)に分ける。各グループごとに関数が別々に値を計算する。つまり、ROW_NUMBER()がウェイターだったら、各「テーブル」(パーティション)ごとに番号を振り直すイメージ。

例:PARTITION BYを使う

SELECT
    department_id,
    employee_id,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department_id) AS row_num
FROM employees;

何が起きてる?

  1. employeesテーブルのデータがdepartment_idの値ごとにグループ分けされる。
  2. 各グループ内でROW_NUMBER()で順番が振られる。

結果:

department_id employee_id salary row_num
1 1 50000 1
1 3 55000 2
2 2 60000 1

ORDER BYで順番を指定する

今度はもうちょっと構造を加えよう。例えば、単に行をナンバリングするだけじゃなくて、給料が高い順に番号を振りたい場合。これはORDER BYで解決できる。

ORDER BYは、ウィンドウ関数がどの順番で行を処理するかを決める。

例:OVER()の中でORDER BYを使う

SELECT
    department_id,
    employee_id,
    salary,
    RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank
FROM employees;

何が起きてる?

  1. データがグループ分けされる(PARTITION BY department_id)。
  2. 各グループ内で給料の高い順に並べ替え(ORDER BY salary DESC)。
  3. 並び順に応じてランクが振られる。

結果:

department_id employee_id salary rank
1 3 55000 1
1 1 50000 2
2 2 60000 1

ウィンドウ関数の組み合わせ

SQLでは、1つのクエリで複数のウィンドウ関数を使えるし、それぞれ独自のルールで動かせる。つまり、同じ部屋で音楽を流しながら人数を数えるみたいな感じで、各プロセスは独立してる!

例:複数のウィンドウ関数

SELECT
    department_id,
    employee_id,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num,
    AVG(salary) OVER (PARTITION BY department_id) AS avg_salary
FROM employees;

何が起きてる?

  1. ROW_NUMBER()は各グループで給料が高い順に行をナンバリング。
  2. AVG()は各グループの平均給料を計算。

結果:

department_id employee_id salary row_num avg_salary
1 3 55000 1 52500
1 1 50000 2 52500
2 2 60000 1 60000

リアルな現場での例

OVER()付きウィンドウ関数は、いろんな現場で使われてる。例えば:

  • 売上分析: 各カテゴリ内で商品の売上数をランキング
  • ランキング: 各グループで学生の平均点から順位を決める
  • 時系列: 時間ごとの売上の累積合計

売上分析の例:

SELECT
    category_id,
    product_id,
    product_name,
    SUM(sales) OVER (PARTITION BY category_id ORDER BY sales DESC) AS cumulative_sales
FROM products;

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

  1. PARTITION BYの指定漏れ

PARTITION BYを使わないと、ウィンドウ関数はテーブル全体に適用される。グループごとに分けたい場合は、思った通りの結果にならないことも。

💡 テーブルをどう分けたいか(ユーザーごと、注文ごと、カテゴリごとなど)をちゃんと指定しよう。


  1. ORDER BYでのデータ型ミス

ウィンドウ関数内のORDER BYはデータ型に敏感。日付をテキスト(VARCHAR)で保存してると、アルファベット順になって時系列順にならないことも。

💡 そういうフィールドは、並べ替える前にちゃんとDATEINTEGERなどに変換しよう。

  1. ROWS BETWEENの使い方ミス

デフォルトだと、ウィンドウ関数はROWS BETWEENで決まる範囲で動く。フレームを明示しないと、RANGEの動きになって、思ったより多くの行が返ることもある。

💡 ちゃんとコントロールしたいなら、ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWを使って、最初から今の行までの累積を作ろう。

  1. NULLの扱いミス

ウィンドウ関数はNULLをいろんな風に扱う。例えば、RANK()DENSE_RANK()NULLも値としてカウントして、別のランクを振る。

💡 ORDER BYNULLS LASTNULLS FIRSTを使って、NULLの位置をコントロールしよう。

  1. 普通の集計でいいのにウィンドウ集計を使う

たまに、普通のGROUP BY集計で十分なのに、ウィンドウ集計関数(SUM() OVER(...)など)を使ってクエリが複雑&遅くなってることがある。

💡 行ごとの詳細を残したい時だけウィンドウ関数を使おう。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION