コマンド道場

$ man rank

RANK / DENSE_RANK

ウィンドウ関数

順位を付ける。同じ値には同じ順位が付く。RANK は次の番号が飛び、DENSE_RANK は飛ばない。

呼び出しの形

RANK() OVER (ORDER BY 式)
DENSE_RANK() OVER (ORDER BY 式)
RANK() OVER (PARTITION BY 式 ORDER BY 式)

つまずきやすいところ

  • 同順位があると RANK は番号が飛びます。「1 位, 2 位, 3 位…」と連番になる前提のコードは壊れます

  • ORDER BY を書かないと順位に意味がなくなります(すべて 1 位になります)。

  • 順位を絞り込みたいときは WHERE r <= 3 を同じ階層に書けません。副問い合わせか WITH で一度列にしてから絞ります。

実行例

結果は実際に流したものです。同じ SQL を 自由に打てる画面 で試すと、同じ結果になります。

RANK と DENSE_RANK を並べて違いを見る
SELECT name, salary, RANK() OVER (ORDER BY salary DESC) AS r, DENSE_RANK() OVER (ORDER BY salary DESC) AS dr FROM shain ORDER BY r, name
namesalaryrdr
鈴木98000011
佐藤72000022
山田70000033
清水65000044
加藤61000055
中村53000066
伊藤53000066
田中52000087

18(うち 8 行を表示)

中村と伊藤が同額(530000)です。ここから先で 2 つの列がずれます。

部署ごとに順位を振り直す
SELECT busho_id, name, salary, RANK() OVER (PARTITION BY busho_id ORDER BY salary DESC) AS r FROM shain ORDER BY busho_id, r
busho_idnamesalaryr
1鈴木9800001
2佐藤7200001
2中村5300002
2田中5200003
2小林4450504
3山田7000001
3伊藤5300002
3高橋4800003

18(うち 8 行を表示)

説明

RANKORDER BY で並べた順に順位を付けます。ROW_NUMBER との違いは、同じ値には同じ順位が付くことです。

同順位のあとの番号の扱いが 2 通りあります。

  • RANK … 1, 2, 2, 4 … と、同順位の数だけ飛びます
  • DENSE_RANK … 1, 2, 2, 3 … と、飛びません

「3 位が何人いるか分からないが、とにかく上位 3 位まで」という要件なら RANK、「上から 3 段階まで」なら DENSE_RANK です。要件の言葉を、どちらの数え方かに翻訳するところが設計の勘どころになります。

PARTITION BY を付ければ、区切りごとに順位を振り直せます。

ほかの製品でも通るか

主要 7 製品で通用 そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database / IBM Db2

製品ごとの対応表(根拠つき)を見る

次に読む

関係する関数

根拠にした一次情報