Kodokon kodokon.com

ウィンドウ関数:ROW_NUMBER、RANK、SUM OVER

OVERとPARTITION BYのおかげで、行をまとめてしまうことなく、順位、累計、グループ内での比較を計算しましょう。

10 分 · 3 問

このレッスンを Kodokon で開く

GROUP BYは行をグループにまとめてしまいますが、ウィンドウ関数すべての行を保ったまま集約や順位を計算します。グループごとの上位N件、累計、自分のチームの平均との比較のためのツールです。SQLiteではバージョン3.25(2018年)から利用できます。データセットを読み込みましょう。

SQL
CREATE TABLE employees (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  dept TEXT NOT NULL,
  salary INTEGER NOT NULL
);

INSERT INTO employees (name, dept, salary)
VALUES
  ('alice', 'tech', 5200),
  ('bruno', 'tech', 4800),
  ('chloe', 'tech', 4800),
  ('david', 'tech', 4100),
  ('emma', 'sales', 3900),
  ('fanny', 'sales', 3600),
  ('gilles', 'sales', 3600),
  ('hugo', 'sales', 3200);
このレッスンのデータセット。

三つの順位付け関数、同順位に直面したときの三つの振る舞い。ROW_NUMBERは重複なしで番号を振り(同順位は任意に決着)、RANKは同順位に同じ順位を与えてから飛ばし(1, 2, 2, 4)、DENSE_RANKは飛ばしません(1, 2, 2, 3)。WINDOW句を使えばウィンドウの定義を繰り返さずに済みます(SQLiteとPostgreSQLはサポート、SQL Serverはサポートしません)。

SQL
SELECT name, salary,
  ROW_NUMBER() OVER w AS row_num,
  RANK() OVER w AS rnk,
  DENSE_RANK() OVER w AS dense
FROM employees
WHERE dept = 'tech'
WINDOW w AS (ORDER BY salary DESC);
brunoとchloe(4800):順位は2と2、そのあとRANKは4に飛ぶ。

PARTITION BYは部分集合ごとに計算をやり直します。部署ごとに一つのウィンドウで、何もまとめません。CTEと組み合わせると、まさに面接で問われるパターンになります。各部署で最も給料の高いN人です。CTEが必須なのは、ウィンドウ関数がWHEREのあとに評価されるからです。rnで直接フィルタリングすることはできません。

SQL
WITH ranked AS (
  SELECT name, dept, salary,
    ROW_NUMBER() OVER (
      PARTITION BY dept
      ORDER BY salary DESC
    ) AS rn
  FROM employees
)
SELECT name, dept, salary
FROM ranked
WHERE rn <= 2;
部署ごとの上位2人:暗記しておくべきパターン。

定番の集約はOVERでウィンドウになります。SUM(salary) OVER (ORDER BY ...)は行ごとに累計を生み出します。厳密に行ごとの累計にするには、ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWでフレームを指定しましょう。

SQL
SELECT name, dept, salary,
  SUM(salary) OVER (
    PARTITION BY dept
    ORDER BY salary DESC
    ROWS BETWEEN UNBOUNDED PRECEDING
         AND CURRENT ROW
  ) AS running_total
FROM employees;
給料の累計、部署ごとにリセットされる。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. 給料が5200、4800、4800、4100のとき、RANK()は4100にどの順位を割り当てますか?
    • 3
    • 4
    • 2
  2. ROW_NUMBER()でフィルタリングするのになぜCTEが必要なのですか?
    • OVERはWHEREのあとに評価されるので、その中では禁止されている
    • CTEは常により効率的だから
    • ROW_NUMBERはWITHの中でしか動かないから
  3. OVER句の中でPARTITION BY deptは何をしますか?
    • 最終結果を部署で並べ替える
    • 部署ごとにウィンドウの計算をやり直す
    • 行を部署ごとに一つにまとめる