PERCENT_RANK是窗口函数,返回0到1间的相对排名,公式为(rank-1)/(total_rows-1);RANK跳号、DENSE_RANK连续,二者均返回整数名次且不归一化,而PERCENT_RANK对相同值赋予相同百分比排名。

PERCENT_RANK 是什么,和 RANK、DENSE_RANK 有什么区别
PERCENT_RANK 返回的是一个归一化的排名值:它把当前行在分组内的相对位置换算成 0 到 1 之间的浮点数(含 0,不含 1),计算公式是 (rank - 1) / (total_rows - 1)。注意分母是整个分组的总行数减 1,不是当前排名减 1。
-
RANK()给相同值分配相同名次,但会跳过后续名次(比如两个第 1 名后直接是第 3 名) -
DENSE_RANK()也给相同值同名次,但不跳号(两个第 1 名后是第 2 名) -
PERCENT_RANK()不关心“名次数字”,只关心“你在所有数据中的相对位置”
所以当你想回答“这个员工绩效处于前多少百分比”时,PERCENT_RANK() 比 RANK() 更直观——它天然就是百分位视角。
怎么写基础语法,WHERE 和 ORDER BY 的顺序很重要
PERCENT_RANK() 是窗口函数,必须搭配 OVER() 子句,且里面至少要有 ORDER BY。它不能出现在 WHERE 或 HAVING 中(因为窗口函数在 WHERE 之后执行),只能用于 SELECT 或 ORDER BY。
SELECT name, score, PERCENT_RANK() OVER (ORDER BY score DESC) AS pr FROM employees;
-
ORDER BY score DESC表示高分排前面,所以最高分的PERCENT_RANK是 0.0 - 如果你用
ASC,最低分才是 0.0,容易误解,建议统一按业务逻辑升序/降序,再看结果解释 - 不加
PARTITION BY就是全表排序;如果要按部门分别算分位数,得写成OVER (PARTITION BY dept ORDER BY score DESC)
遇到 NULL 值会怎样,怎么控制它的排序位置
PERCENT_RANK() 本身不报错,但 NULL 在排序中默认排最前(NULLS FIRST)或最后(NULLS LAST),不同数据库默认不同:
- PostgreSQL 默认
NULLS FIRST(升序时 NULL 最小,降序时最大) - MySQL 8.0+ 和 SQL Server 把 NULL 当最小值处理(即
DESC下排最后) - Oracle 同样默认
NULLS LAST(可显式指定)
如果你的 score 字段有 NULL,又希望它们不参与分位计算,得先过滤:
SELECT name, score, PERCENT_RANK() OVER (ORDER BY score DESC) AS pr FROM employees WHERE score IS NOT NULL;
或者用 CASE 把 NULL 映射为极小/极大值,但不如直接过滤干净。
怎么解释结果:0.0 不代表“第一”,而是“并列第一里最靠前的那个”
假设 5 个员工分数是 [95, 95, 87, 82, 76]:
- 两个 95 分都会得到
PERCENT_RANK = 0.0(因为 rank = 1,(1−1)/(5−1) = 0) - 87 分 rank = 3 → (3−1)/(5−1) = 0.5
- 76 分 rank = 5 → (5−1)/(5−1) = 1.0?不对——等等,
PERCENT_RANK最大值永远是(n−1)/(n−1) = 1.0?其实不是:它定义是(rank − 1) / (total_rows − 1),而 rank 是按DENSE_RANK还是RANK算?答案是:按实际行序(即等价于RANK,跳号)。所以上例中两个 95 共享 rank 1,87 是 rank 3,82 是 rank 4,76 是 rank 5 → 76 的PERCENT_RANK确实是 1.0。
但注意:如果有 100 行,最后一行 PERCENT_RANK 是 1.0;只有 1 行时,分母为 0,多数数据库返回 NULL(PostgreSQL 报错,MySQL 返回 NULL)。
所以别把 0.0 理解成“绝对第一”,它只表示“在排序序列中最靠前的位置”。真正想表达“前 10%”,得结合 PERCENT_RANK ,而不是看是否等于 0.0。
分位数边界模糊、空值处理、单行异常、跨数据库排序行为差异——这些才是上线前最容易漏掉的点。

















