PERCENT_RANK返回0.0–1.0的相对位置比例,公式为(rank−1)/(分组总行数−1),首行恒为0.0,末行仅当分组≥2时为1.0;单行分组结果为NULL,不反映值分布占比而仅表位置序。

PERCENT_RANK 能直接算出某行在分组内的相对排名百分比,但它的结果不是“第几百分位”,而是“比多少比例的行小”,且首行固定为 0 —— 这一点不注意会误读结果。
PERCENT_RANK 的计算逻辑和边界值
它按 ORDER BY 排序后,对第 n 行返回 (n-1) / (总行数-1)。这意味着:
- 第一行永远是
0.0(无论是否并列) - 最后一行永远是
1.0(只要总行数 ≥ 2) - 并列值共享相同排序位置,但
PERCENT_RANK不做额外去重或平均处理,只看位置索引 - 如果只有 1 行,结果为
NULL(分母为 0)
和 RANK()、DENSE_RANK() 的关键区别
三者都依赖排序,但语义完全不同:
-
RANK():并列时跳号(如 1,1,3),但PERCENT_RANK仍按实际位置算(如位置 1,1,3 → 百分比为 0.0, 0.0, 1.0) -
DENSE_RANK():并列不跳号(如 1,1,2),但PERCENT_RANK不感知这个“稠密序”,只认物理顺序中的位置 - 真正影响
PERCENT_RANK结果的,只有ORDER BY子句定义的行序,以及窗口内总行数
实际使用时必须加 PARTITION BY 吗?
不加也可以,但几乎总是需要:
- 不加
PARTITION BY→ 全表作为一个窗口 → 所有行参与排序和分母计算 - 加了
PARTITION BY category→ 每个 category 内独立算PERCENT_RANK,分母是该 category 行数减 1 - 漏写
PARTITION BY却以为是按组排名,是常见误解来源;比如想看“每部门薪资百分比排名”,却忘了分区,结果变成全公司统一排名
示例:
SELECT name, salary,<br> PERCENT_RANK() OVER (PARTITION BY dept ORDER BY salary) AS pct_rank<br>FROM employees;
替代方案:当 PERCENT_RANK 不符合业务需求时
如果你要的是“该值超过多少比例的同行”,但又不想首行为 0、末行为 1,可考虑:
- 用
ROW_NUMBER() OVER (...) - 1除以COUNT(*) OVER (...)自己算,控制分子分母逻辑 - 用
CUME_DIST():它返回“≤当前值的行占比”,首行不一定为 0(取决于是否有并列),语义更接近“累积分布” - 数据库不支持窗口函数(如旧版 MySQL)?只能用自连接或变量模拟,性能差且易出错
真正难的不是写对语法,而是确认业务上到底要“位置占比”还是“值分布占比”——PERCENT_RANK 只回答前者。

















