PERCENT_RANK返回0到1间小数,因其按公式(rank−1)/(total_rows−1)计算相对位置:首行恒为0.0,末行在窗口行数>1时恒为1.0,单行窗口结果为NULL;它反映“比多少比例的同行靠前”,非整数百分比或分位数值。

PERCENT_RANK 为什么返回 0 和 1 之间的小数?
PERCENT_RANK 不是四舍五入后的整数排名,而是按公式 (rank - 1) / (total_rows_in_window - 1) 计算的相对位置。它把排序后的行映射到 [0, 1] 区间:首行恒为 0.0,末行在窗口行数 > 1 时恒为 1.0;若窗口只有 1 行,则分母为 0,结果为 NULL。
常见误解是把它当“前百分之几”,其实它反映的是“比多少比例的同行靠前”。比如 PERCENT_RANK = 0.75 意味着该行排在前 75% 的位置(即有 75% 的行排在它前面或并列)。
分组内计算必须用 PARTITION BY,否则全表排序
不加 PARTITION BY 时,PERCENT_RANK() 默认对整个结果集排序,不是你想要的“每组独立排名”。例如按部门分组求薪资百分位,必须显式写:
SELECT dept, salary,
PERCENT_RANK() OVER (PARTITION BY dept ORDER BY salary) AS pct_rank
FROM employees;注意:PARTITION BY 的字段必须出现在 SELECT 或 GROUP BY 中(取决于上下文),否则可能报错或逻辑错乱。
- 如果漏写
PARTITION BY,所有员工混在一起排,DEPT = 'HR'的人可能得到0.92—— 这其实是全公司第 92 百分位,不是 HR 部门内的 -
ORDER BY必须存在,否则语法错误;升序(默认)和降序(ORDER BY salary DESC)结果完全相反 - 空值(
NULL)默认排在最前(NULLS FIRST),若想排最后需显式写ORDER BY salary NULLS LAST
和 RANK、DENSE_RANK 的关键区别在哪?
PERCENT_RANK 的分母固定为当前窗口总行数减 1,而 RANK 和 DENSE_RANK 是基于“名次”计算,不归一化。三者对相同数据会给出不同数值:
假设某组 5 人薪资为 [5000, 6000, 6000, 7000, 8000]:
-
RANK()→[1, 2, 2, 4, 5](并列后跳号) -
DENSE_RANK()→[1, 2, 2, 3, 4](并列后不跳号) -
PERCENT_RANK()→[0.0, 0.25, 0.25, 0.75, 1.0](按公式算,分母恒为 4)
特别注意:并列值的 PERCENT_RANK 完全相同,且不会因为并列而“挤占”后续值的百分位——这是它和 CUME_DIST 的核心差异(后者累计分布会受并列影响)。
实际使用时最容易被忽略的兼容性问题
MySQL 8.0+、PostgreSQL 8.4+、SQL Server 2005+、Oracle 9i+ 支持 PERCENT_RANK,但 SQLite 和旧版 MySQL(
另一个坑是:某些 BI 工具(如早期 Tableau 版本)解析窗口函数时,若未显式指定 OVER 子句中的排序方向,可能默认忽略或报错。务必在 SQL 层写全 ORDER BY。
还有就是别拿 PERCENT_RANK = 0.9 直接当“前 10%”,它只是相对位置;真要取前 10%,应该用 CUME_DIST() >= 0.9 或配合 NTILE(10) 分桶更稳妥。

















