percent_rank返回排序位置的相对排名,计算公式为(r-1)/(n-1),范围0.0到1.0(含0.0,n>1时含1.0),最小值恒为0.0,单行窗口返回null,相同值排名相同且不跳号。

PERCENT_RANK 在 SQL Server 中返回的是基于排序位置的相对排名,不是「该值在整个数据集中占比多少」,而是「比它小的值占多大比例」——结果范围是 0.0 到 1.0(含 0.0,不含 1.0),且最小值永远对应 0.0。
PERCENT_RANK 的计算逻辑和边界行为
它按 ORDER BY 排序后,对第 r 行(从 1 开始计数)计算:(r - 1) / (n - 1),其中 n 是窗口内总行数。这意味着:
- 第一行(最小值)一定是
0.0,因为(1 - 1) / (n - 1) = 0 - 最后一行(最大值)**不是**
1.0,而是(n - 1) / (n - 1) = 1.0—— 等等,这里要特别注意:只有当n > 1时,最后一行才是1.0;如果窗口只有 1 行,分母为 0,SQL Server 会返回NULL - 相同值会得到相同的
PERCENT_RANK,且后续排名不会跳过(比如两个并列第 2 名,下一个是第 4 名?不,PERCENT_RANK不这样算)
和 CUME_DIST、RANK 的关键区别
容易混淆的是这三个函数都跟「累积」和「排序」有关,但语义完全不同:
-
CUME_DIST()计算「小于等于当前值的行数占比」,结果范围是1.0/n到1.0,且最大值恒为1.0 -
RANK()和DENSE_RANK()返回整数名次,不归一化,也不反映比例 -
PERCENT_RANK()**只关心位置,不关心值重复与否的密度**——哪怕前 90 行全一样,只要它们排在最前面,它们的PERCENT_RANK都是0.0(因为r = 1)
实际写法与常见陷阱
必须搭配 OVER 子句,且 ORDER BY 不可省略;不能单独用在 WHERE 或 HAVING 中(需子查询或 CTE):
SELECT score, PERCENT_RANK() OVER (ORDER BY score) AS pct_rank FROM exam_results;
常见踩坑点:
- 误以为
PERCENT_RANK = 0.5就代表中位数——不一定,它只说明该行是第(n+1)/2个位置(若n为奇数),但值未必等于中位数值 - 在分组窗口(
PARTITION BY)里忘了检查每组的n是否 ≥ 2,否则单行组会得NULL - 把
PERCENT_RANK当作「百分位数(percentile)」使用——这是两个概念:PERCENT_RANK是 rank-based,而PERCENTILE_CONT才是插值法求某百分位的值
什么时候该用 PERCENT_RANK,什么时候不该
适合场景:快速标定某条记录在有序序列中的「相对位置感」,比如「这个销售额排在所有门店的前 15% 吗?」(查 pct_rank )
- 需要解释性排名(如报表中显示“高于 87% 的同行”)→ 用
PERCENT_RANK - 需要找具体阈值(如“取前 10% 的销售值是多少?”)→ 改用
PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY sales) - 要做分位数分桶(如 quintiles)→ 更稳妥的方式是用
NTILE(5),它强制均分,不受重复值干扰
真正容易被忽略的是:它的分母依赖于窗口总行数,而不是去重后的唯一值个数;所以当数据存在大量重复极值时,PERCENT_RANK 会密集挤在两端,中间可能大片空白——这不是 bug,是设计使然。










