percent_rank() 返回0到1间的连续百分比值,计算公式为(n-1)/(total_rows-1),相同值不同行结果不同;必须配合over(order by ...)使用,不可缺order by;转整数百分比宜用floor/ceil而非round。

PERCENT_RANK() 能算出每个值在分组内的相对排名百分比,但结果不是四舍五入的整数,而是 0 到 1 之间的连续值(最小值对应 0,最大值不一定为 1)。
PERCENT_RANK() 的计算逻辑和边界行为
它按 ORDER BY 排序后,对第 n 行返回 (n-1) / (total_rows - 1)。注意:分母是整个窗口的总行数减 1,不是当前值的频次;即使有重复值,每行仍独立计算,不会合并排名。
- 如果只有 1 行数据,
PERCENT_RANK()返回0(因为(1-1)/(1-1)未定义,MySQL 规定此时强制为 0) - 相同值不同行会得到不同百分比——比如 5 行中第 2、3 行值相同,它们的
PERCENT_RANK()分别是0.25和0.5,不是“并列 25%” - 想实现“相同值同排名”,该用
RANK()或DENSE_RANK(),再手动换算,PERCENT_RANK()本身不支持去重归一
常见写法与易错参数组合
必须搭配 OVER() 使用,且 ORDER BY 是必需子句——漏写会报错 ERROR 3589 (HY000): Window '<name>' lacks an ORDER BY clause</name>。
- 基础写法:
PERCENT_RANK() OVER (ORDER BY score DESC)—— 全表按 score 降序排百分比 - 分组内计算:
PERCENT_RANK() OVER (PARTITION BY dept_id ORDER BY salary ASC)—— 每个部门单独算薪资百分位 - 错误示范:
PERCENT_RANK() OVER (PARTITION BY dept_id)—— 缺少ORDER BY,直接报错 - 性能提示:若
ORDER BY字段无索引,大表上执行会触发 filesort,可能显著拖慢查询
如何把 PERCENT_RANK() 结果转成“第 X%”的整数形式
原始结果是小数(如 0.75),业务常需展示为 75% 或四舍五入的整数排名。不能直接 ROUND(x * 100) 就完事——因为 PERCENT_RANK() 最大值永远小于 1(除非仅一行),所以 ROUND(PERCENT_RANK() * 100) 可能永远得不到 100。
- 安全转整数百分比:
FLOOR(PERCENT_RANK() * 100)或CEIL(PERCENT_RANK() * 100),根据业务取舍向下/向上取整 - 补足 100% 场景:
IF(ROW_NUMBER() OVER w = COUNT(*) OVER w, 100, FLOOR(PERCENT_RANK() OVER w * 100)),其中w是已定义的命名窗口 - 避免浮点误差:MySQL 8.0.17+ 支持
ROUND(x, 0),但旧版建议用FLOOR(x * 100 + 0.5)模拟四舍五入
真正容易被忽略的是:当数据存在大量重复值时,PERCENT_RANK() 依然严格按行号线性分配百分比,和人直观理解的“百分位数(percentile)”不是一回事——比如 P95 应该表示 ≥95% 数据的值,而 PERCENT_RANK() 输出的是“位置占比”。需要近似 percentile 值,得用 PERCENTILE_CONT() 或手动插值。











