<p>PERCENT_RANK() 返回当前行在分组内的相对位置,计算公式为(rank - 1) / (total_rows - 1),首行为0.0、末行为1.0(≥2行时),单行返回NULL。</p>

PERCENT_RANK() 函数到底返回什么值
PERCENT_RANK() 不是四舍五入后的百分比,也不是按 1–100 映射的整数,它返回的是「当前行在分组内的相对位置」,计算公式为:(rank - 1) / (total_rows - 1)。其中 rank 是 ORDER BY 下的并列排名(即 RANK() 值),total_rows 是该窗口内总行数。
这意味着:第一行永远是 0.0,最后一行永远是 1.0(只要窗口内至少有 2 行);如果只有 1 行,结果是 NULL(因为分母为 0)。
常见误用场景:直接把结果乘以 100 再加 % 符号就当“百分比排名”展示——这没问题,但要注意它不代表“前 X%”,而是“位于从最低到最高的第 X% 位置”。
窗口定义必须显式写 PARTITION BY 吗
不必须,但几乎总是需要。省略 PARTITION BY 会让整个结果集变成一个窗口,PERCENT_RANK() 就在整个表上计算,容易掩盖业务逻辑粒度(比如你想看每个部门内员工薪资的相对位置,而不是全公司统一排)。
典型错误:
SELECT name, salary, PERCENT_RANK() OVER (ORDER BY salary) AS pct_rank FROM employees;
这会把 CEO 和实习生放在同一窗口里算,结果失真。正确做法是:
- 按业务维度加
PARTITION BY department - 排序字段要和业务意义一致(如
ORDER BY salary DESC表示“越高越靠前”,此时PERCENT_RANK()值越小代表越优秀) - 注意 NULL 默认排在最前(ASC)或最后(DESC),会影响首尾的
0.0/1.0分布
和 CUME_DIST()、RANK() 的关键区别在哪
三者都依赖排序,但语义不同,混用会出错:
-
RANK()返回整数名次(并列时跳号,如 1,1,3) -
CUME_DIST()计算“≤当前值的行数占比”,结果范围是(0, 1],且不会出现0.0 -
PERCENT_RANK()强制首尾为0.0/1.0,中间线性插值,更适合做归一化坐标轴
实操建议:要做“前 10% 高绩效员工”筛选,别用 PERCENT_RANK() ——因为 <code>PERCENT_RANK() 是基于名次推导,不是分布统计;此时应优先考虑 CUME_DIST() 或直接用 NTILE(10) 分十分位。
MySQL 8.0+ 和 PostgreSQL 的兼容性细节
PERCENT_RANK() 在 MySQL 8.0+、PostgreSQL 8.4+、SQL Server 2005+、Oracle 9i+ 均支持,但行为有一处关键差异:
PostgreSQL 允许在 ORDER BY 子句中使用表达式(如 ORDER BY salary * 1.2),而 MySQL 要求排序字段必须是 SELECT 列或原始列名(不能是计算列别名,除非重复写表达式)。
易踩坑点:
- MySQL 中写
PERCENT_RANK() OVER (ORDER BY adjusted_salary)(其中adjusted_salary是 SELECT 中定义的别名)→ 报错Unknown column 'adjusted_salary' - 正确写法是重复表达式:
PERCENT_RANK() OVER (ORDER BY salary * 1.2) - PostgreSQL 可直接用别名,但注意子查询中别名作用域限制
跨数据库移植时,优先用原始字段或确定性表达式,避免依赖别名。

















