percent_rank是窗口函数,计算公式为(当前行排名-1)/(分组总行数-1),结果在0到1之间;需配合over()使用,order by方向影响语义,如成绩排名应desc,且自动处理并列。

PERCENT_RANK 是什么,它怎么算出来的
PERCENT_RANK 不是简单把名次除以总人数,而是按「当前行在分组内的相对位置」计算:(当前行的排名 - 1) / (分组总行数 - 1)。注意分母是 count(<em>) - 1</em>,不是 count();所以最小值一定是 0(第一名),最大值不一定是 1 —— 只有当该组恰好只有一行时才是 1,否则最大值是 (n-1)/(n-1) = 1,但前提是最后一行是唯一排名末位;如果有并列,最后几个相同分数的 PERCENT_RANK 值会一样,且小于 1。
- 它默认按
ORDER BY列升序排序,分数越低,PERCENT_RANK越小(即“排在前面”不代表成绩好) - 如果你评估的是考试成绩,通常希望高分对应高百分位,那就得用
ORDER BY score DESC - 窗口函数必须搭配
OVER(),不能单独写PERCENT_RANK()
怎么写一个正确的成绩百分比排名 SQL
核心是明确三件事:按什么排序、是否分区、是否处理并列。学生成绩一般不分区(全班统一排),但要注意排序方向:
SELECT name, score, PERCENT_RANK() OVER (ORDER BY score DESC) AS pct_rank FROM students;
-
ORDER BY score DESC才能让 95 分的人比 65 分的人得到更高的PERCENT_RANK - 如果漏掉
DESC,95 分反而得到接近 0 的值,容易误读为“垫底” -
PERCENT_RANK自动处理并列:相同分数共享同一排名,但公式中仍用实际行号(row number)参与计算,所以并列者会得到相同结果
和 RANK()、DENSE_RANK() 混用时的常见错觉
PERCENT_RANK 和 RANK() 看似都处理排名,但逻辑完全不同:
-
RANK()返回整数名次(如 1, 2, 2, 4),跳过重复后的数字 -
PERCENT_RANK()返回 0~1 之间的小数,不跳、不整数化,也不代表“前 X%” - 两者窗口定义必须一致,否则对比无意义。例如下面写法是错的:
-- ❌ 错误:两个函数 ORDER BY 方向不一致 PERCENT_RANK() OVER (ORDER BY score ASC), RANK() OVER (ORDER BY score DESC)
- 实际业务中,如果想说“该生成绩超过全班 XX% 的人”,
PERCENT_RANK的值更贴近这个语义(但注意它不四舍五入,也不转百分数,需手动 *100 并ROUND())
MySQL 8.0+ 和 PostgreSQL 的兼容性提醒
PERCENT_RANK 在 MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持,但 SQLite 不支持。使用前务必确认版本:
- MySQL 5.7 或更低版本会报错:
FUNCTION xxx.PERCENT_RANK does not exist - PostgreSQL 中无需额外配置,但若用在子查询或 CTE 里,记得外层别忘了
ORDER BY控制最终输出顺序 - 如果遇到
Window function is not allowed in this context,说明你把它用在了不支持窗口函数的地方(比如旧版 MySQL 的 GROUP BY 子句、或某些聚合嵌套中)
真正容易被忽略的是:它返回的是比例,不是百分比;显示成“85%”要自己乘 100 并格式化,而且它对并列的处理方式会让多个学生拿到完全相同的 PERCENT_RANK 值——这不是 bug,是设计如此。











