percent_rank的计算逻辑是(rank - 1) / (total_rows - 1),其中rank为rank()结果,total_rows为窗口内总行数,故最小值恒为0、最大值恒为1,且相同值因rank并列而结果相同。

PERCENT_RANK函数的计算逻辑是什么?
PERCENT_RANK 不是简单地按值大小排个名再除以总行数。它算的是「当前行在分组内的相对位置」,公式固定为:(rank - 1) / (total_rows - 1),其中 rank 是按 ORDER BY 得到的重复值并列排名(即 RANK() 的结果),total_rows 是该窗口内总行数。
关键点在于:分母永远是 total_rows - 1,所以最小值的 PERCENT_RANK 恒为 0,最大值恒为 1 —— 即使只有一行,结果也是 0;两行时,第一行为 0,第二行为 1;三行时,中间那行就是 0.5。
为什么相同值的PERCENT_RANK结果一样?
因为 PERCENT_RANK 基于 RANK(),不是 ROW_NUMBER()。重复值会获得相同 RANK,进而算出相同 PERCENT_RANK。
- 比如数据
[10, 20, 20, 30],RANK是[1, 2, 2, 4],total_rows = 4 - 两个 20 的
PERCENT_RANK都是(2 - 1) / (4 - 1) = 0.333... - 注意:第 4 行的
RANK是 4(跳过 3),不是 3 —— 这直接影响分子
窗口定义错误会导致结果完全失真
没写 OVER 子句会报错;写错 PARTITION BY 或漏掉 ORDER BY 会让结果失去意义。
-
ORDER BY必须存在,否则语法错误:PERCENT_RANK() OVER ()不合法 - 如果只写
PARTITION BY category但没ORDER BY,数据库会拒绝执行 - 误用
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW等帧子句——PERCENT_RANK不支持帧定义,加了会报错
正确写法只有:PERCENT_RANK() OVER (PARTITION BY ... ORDER BY ...)
和CUME_DIST、NTILE对比时容易混淆
PERCENT_RANK 和 CUME_DIST 都返回 0–1 区间值,但算法不同:CUME_DIST 是「≤当前值的行数 / 总行数」,所以最小值结果是 1 / total_rows,不是 0;而 NTILE(100) 是强行分桶,不是连续比例。
- 对单行数据:
PERCENT_RANK = 0,CUME_DIST = 1.0 - 对重复值:
CUME_DIST在重复值处会跳变(如两个最大值,CUME_DIST都是 1.0),PERCENT_RANK则保持相同中间值 - 想模拟「第90百分位数」不要用
PERCENT_RANK > 0.9直接过滤——它不保证恰好 10% 数据落在后面,建议配合PERCENTILE_CONT使用
真正要取某个百分位的阈值,PERCENT_RANK 只适合做排序定位,别拿来当分位数切割依据。











