PERCENT_RANK() 返回0.0到1.0之间的相对位置秩,公式为(rank−1)/(total_rows−1),首行恒为0.0、末行恒为1.0(行数>1时),单行返回0.0;它反映有序队列中的归一化位置,非人数占比或百分比排名。

PERCENT_RANK() 返回的是什么值?
PERCENT_RANK() 不是百分比排名,而是“相对位置秩”,范围固定在 0.0 到 1.0 之间(含端点)。它按升序排序后,对第 n 行计算:(n - 1) / (total_rows - 1)。第一行永远是 0.0,最后一行永远是 1.0,中间值线性插值。
- 如果只有 1 行数据,
PERCENT_RANK()返回0.0(不是NULL) - 它不处理并列:相同分数的员工可能得到不同
PERCENT_RANK()值(取决于排序稳定性或ORDER BY的二级键) - 想要“并列同分同排名”,得换用
RANK()或DENSE_RANK(),再手动归一化
怎么写才能让绩效排名有意义?
绩效评估中,直接对原始分数跑 PERCENT_RANK() 往往失真——比如销售团队里 90% 的人业绩集中在 85–92 分,剩下 10% 是 40–60 分,这时中间段的 PERCENT_RANK() 差值极小(如 0.421 和 0.423),业务上无法区分。
- 必须明确
ORDER BY的字段是绩效核心指标,例如:ORDER BY quarterly_score DESC(降序:高分排前面,PERCENT_RANK()小值代表更优) - 若需跨部门比较,必须加
PARTITION BY department,否则全公司混排会淹没部门内相对表现 - 避免在
WHERE过滤后再算PERCENT_RANK():它基于当前结果集行数计算,删掉低绩效员工会导致剩下的人PERCENT_RANK()全体上浮,失真
SELECT
employee_id,
department,
quarterly_score,
PERCENT_RANK() OVER (
PARTITION BY department
ORDER BY quarterly_score DESC
) AS pr_rank
FROM performance_review;
和 CUME_DIST()、NTILE(100) 有什么实际区别?
PERCENT_RANK() 和 CUME_DIST() 都返回 0.0–1.0,但算法不同:CUME_DIST() 是“≤当前值的行数 / 总行数”,所以不会出现 0.0(除非全 NULL),且并列时返回相同值;而 PERCENT_RANK() 对并列不友好。
-
NTILE(100)是强行把数据切成 100 组,每组行数尽量均等,但无法保证每组跨度一致——比如 103 行数据,NTILE(100)会产出 3 个100和 100 个其他值,根本不是百分位。 - 真正对标“前 10%”这种业务需求,应优先用
CUME_DIST()>0.9,而不是PERCENT_RANK()0.1(后者漏掉第一名)
容易被忽略的 NULL 和类型陷阱
PERCENT_RANK() 默认把 NULL 当作最小值(ORDER BY ... ASC 时排最前,DESC 时排最后),但不同数据库行为不一致:PostgreSQL 把 NULLS FIRST/LAST 当显式选项,而 SQL Server 强制 NULL 最小。
- 显式声明
NULLS LAST(如果支持)或先用COALESCE(score, 0)填充,避免绩效为 NULL 的员工挤占排名头部 - 输入字段类型必须是可排序的数值或日期;用字符串存分数(如
'92.5')会导致字典序排序:'100''92.5' - 窗口函数不能出现在
WHERE或GROUP BY中,必须放在SELECT或子查询里,否则报错"window function not allowed"
真实场景里,PERCENT_RANK() 最适合做横向对比基线(比如看某员工在本季度所有参评者中的相对位置),但一旦涉及晋升、奖金档位这类“硬切分”,就得立刻切换到 CUME_DIST() 或预定义分位阈值。










