PERCENT_RANK是返回0到1之间归一化排名的窗口函数,公式为(ROW_NUMBER()-1)/(总行数-1);与RANK不同,它不跳过重复值后的排名,且必须用ORDER BY。

PERCENT_RANK 是什么,它和 RANK/ROW_NUMBER 有什么区别
PERCENT_RANK 返回的是一个归一化的排名值,范围在 0 到 1 之间(含 0,不含 1),表示当前行在分组内的相对位置。它不是简单地把 ROW_NUMBER() 除以总数,而是按公式:(当前行的 ROW_NUMBER() - 1) / (总行数 - 1) 计算。
-
PERCENT_RANK对重复值会给出相同结果(因为基于ROW_NUMBER而非RANK) - 它必须配合
OVER子句使用,且ORDER BY是必需的,否则报错:Window function PERCENT_RANK requires ORDER BY - 和
RANK()不同,PERCENT_RANK不跳过后续排名:[10,20,20,30] 的PERCENT_RANK是 [0.0, 0.333, 0.333, 1.0],不是 [0.0, 0.333, 0.333, 0.666]
基础用法:单列排序 + 全局百分比排名
直接对某列排序后计算全局排名:
SELECT score, PERCENT_RANK() OVER (ORDER BY score) AS pct_rank FROM exam_scores;
- 第一行一定是 0.0(因为
ROW_NUMBER= 1 → (1−1)/(n−1) = 0) - 最后一行一定是 1.0(仅当 n > 1;若只有一行,结果是 0.0,因为分母为 0,多数数据库如 PostgreSQL/SQL Server 会返回 0.0,MySQL 8.0+ 同样如此)
- 如果想保留小数位,可套
ROUND(PERCENT_RANK() OVER (...), 3)
分组内计算:用 PARTITION BY 划定排名范围
比如按班级分别算学生成绩的百分比排名:
SELECT class, student_name, score, ROUND(PERCENT_RANK() OVER (PARTITION BY class ORDER BY score), 3) AS class_pct_rank FROM students;
-
PARTITION BY class表示每个班级单独计算,互不影响 - 注意:如果某班只有 1 人,
PERCENT_RANK结果为 0.0(不是 NULL,也不是 1.0) - 若需排除空值影响,加
WHERE score IS NOT NULL,否则NULL默认排最前(取决于ORDER BY ... NULLS FIRST/LAST,不同数据库默认行为不同)
常见踩坑点:ORDER BY 方向、NULL 处理与数据类型
- 默认
ORDER BY score 是升序,最小值得 0.0;如需“高分排前面”,写成 ORDER BY score DESC
-
NULL 值参与排序时:
- PostgreSQL 默认
NULLS LAST(升序时排末尾),但 PERCENT_RANK 仍按实际位置计算,可能导致你预期外的 0.0 出现在最后
- SQL Server 和 MySQL 8.0+ 默认
NULLS FIRST(升序时排最前),此时第一个 PERCENT_RANK 就是 0.0,哪怕它是 NULL
- 输入列类型不影响函数运行,但若
ORDER BY 列有混合类型(如字符串数字 '1', '10', '2'),字典序会导致排名错乱,应先转为数值:ORDER BY CAST(score_str AS INT)
ORDER BY score 是升序,最小值得 0.0;如需“高分排前面”,写成 ORDER BY score DESC
NULL 值参与排序时:- PostgreSQL 默认
NULLS LAST(升序时排末尾),但PERCENT_RANK仍按实际位置计算,可能导致你预期外的 0.0 出现在最后 - SQL Server 和 MySQL 8.0+ 默认
NULLS FIRST(升序时排最前),此时第一个PERCENT_RANK就是 0.0,哪怕它是NULL
ORDER BY 列有混合类型(如字符串数字 '1', '10', '2'),字典序会导致排名错乱,应先转为数值:ORDER BY CAST(score_str AS INT)
窗口函数的边界很实在:没 ORDER BY 就报错,没 PARTITION BY 就全表算,而 NULL 的位置和数据库实现强相关——别靠猜测,查文档或显式写 NULLS LAST。










