$ man percent-rank
PERCENT_RANK / CUME_DIST
ウィンドウ関数順位を 0〜1 の割合で返す。2 つは端の扱いが違う。
呼び出しの形
PERCENT_RANK() OVER (ORDER BY 式) CUME_DIST() OVER (ORDER BY 式)
つまずきやすいところ
PERCENT_RANKは最初の行が必ず 0、CUME_DISTは 0 より大きい値から始まります。「上位何 %」を出すならCUME_DISTのほうが素直です。1 行しかない区切りでは
PERCENT_RANKが 0 になります。区切りごとに計算していると、少数の区切りで値が跳ねます。戻り値は
double precisionです。表示桁をそろえるならnumericに変換してから丸めてください。
実行例
結果は実際に流したものです。同じ SQL を 自由に打てる画面 で試すと、同じ結果になります。
SELECT name, salary, ROUND(PERCENT_RANK() OVER (ORDER BY salary DESC)::numeric, 3) AS percent_rank, ROUND(CUME_DIST() OVER (ORDER BY salary DESC)::numeric, 3) AS cume_dist FROM shain ORDER BY salary DESC
| name | salary | percent_rank | cume_dist |
|---|---|---|---|
| 鈴木 | 980000 | 0.000 | 0.056 |
| 佐藤 | 720000 | 0.059 | 0.111 |
| 山田 | 700000 | 0.118 | 0.167 |
| 清水 | 650000 | 0.176 | 0.222 |
| 加藤 | 610000 | 0.235 | 0.278 |
| 伊藤 | 530000 | 0.294 | 0.389 |
| 中村 | 530000 | 0.294 | 0.389 |
| 田中 | 520000 | 0.412 | 0.444 |
18 行(うち 8 行を表示)
PERCENT_RANK は先頭が 0、CUME_DIST は 0 より大きい値から始まります。
SELECT busho_id, name, ROUND(PERCENT_RANK() OVER (PARTITION BY busho_id ORDER BY salary DESC)::numeric, 3) AS pr, ROUND(CUME_DIST() OVER (PARTITION BY busho_id ORDER BY salary DESC)::numeric, 3) AS cd FROM shain ORDER BY busho_id, pr
| busho_id | name | pr | cd |
|---|---|---|---|
| 1 | 鈴木 | 0.000 | 1.000 |
| 2 | 佐藤 | 0.000 | 0.250 |
| 2 | 中村 | 0.333 | 0.500 |
| 2 | 田中 | 0.667 | 0.750 |
| 2 | 小林 | 1.000 | 1.000 |
| 3 | 山田 | 0.000 | 0.250 |
| 3 | 伊藤 | 0.333 | 0.500 |
| 3 | 高橋 | 0.667 | 0.750 |
18 行(うち 8 行を表示)
1 行しかない区切りでは PERCENT_RANK が 0、CUME_DIST が 1 になります。
説明
順位を件数によらない割合で表す関数です。母数が違う集団を比べるときに使います。
PERCENT_RANK…(順位 - 1) ÷ (総行数 - 1)。最初の行が必ず 0、最後が 1CUME_DIST… 累積分布。0 より大きく 1 以下で、最後が必ず 1
「上位何 % か」を出したいなら CUME_DIST、「先頭からの相対位置」なら PERCENT_RANK が素直です。
⚠️ 同じ値には同じ割合が付きます。 位置ではなく順位を使うためで、NTILE のように同値が別のグループへ分かれることはありません。
⚠️ 行が 1 行しかないとき、PERCENT_RANK は 0 になります(分母が 0 になるので特別扱いされます)。CUME_DIST は 1 です。区切りごとに計算していると、1 件だけの区切りで値が跳ねるので注意してください。
次に読む
根拠にした一次情報
- PostgreSQL 18 マニュアル: ウィンドウ関数(2026-09-02 確認)