CUME_DIST返回“≤当前值的行数÷分组总行数”,范围(0,1]且最小值为1/N;PERCENT_RANK为(rank−1)/(N−1),首行为0、末行必为1,重复值共享rank但结果可能不同。

什么是 CUME_DIST,它和 PERCENT_RANK 有什么区别
CUME_DIST 返回当前行在分组内的「小于等于该行值的行数占比」,结果范围是 (0, 1],且最小值一定是 1/N(N 为分组总行数)。它不跳过重复值,所有相同绩效的员工会得到相同的累积分布值——这点常被误认为是“排名”,其实它统计的是「覆盖比例」。
对比 PERCENT_RANK:后者是 (rank−1)/(N−1),首行必为 0,末行必为 1;而 CUME_DIST 首行 ≥1/N,末行恒为 1。当有重复绩效时,CUME_DIST 的值会「阶梯式上升」,不是线性变化。
基本写法:必须带 ORDER BY,不能单独用 PARTITION BY
CUME_DIST() 是窗口函数,括号内永远为空,排序和分组全靠 OVER 子句。漏掉 ORDER BY 会报错(如 PostgreSQL 报 ERROR: window function CUME_DIST requires an ORDER BY clause)。
常见错误写法:CUME_DIST() OVER (PARTITION BY dept) —— 缺少 ORDER BY,语法非法。
正确写法示例(按绩效降序计算全公司累积分布):
SELECT name, performance,
CUME_DIST() OVER (ORDER BY performance DESC) AS cume_dist
FROM employees;- 升序(
ASC)对应「从低到高」的累积,即低绩效员工的cume_dist值小;降序更符合「绩效越高越靠前」的业务直觉 - 若要按部门分别计算,必须同时写
PARTITION BY dept ORDER BY performance DESC - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 不支持
处理并列绩效时的结果怎么看
假设有 5 名员工,绩效分别为 [85, 90, 90, 92, 95],按降序排列后:
performance | cume_dist ---------- | ---------- 95 | 1.0 ← 最高者,覆盖全部 5 行 92 | 0.8 ← ≤92 的有 4 行(95/92/90/90) 90 | 0.6 ← ≤90 的有 3 行(90/90/85),注意两个 90 得到相同值 90 | 0.6 ← 同上 85 | 0.2 ← ≤85 的只有自己,1/5 = 0.2
关键点:
- 重复值不会导致「跳跃」,而是共享同一累积比例
- 结果不是「排名百分比」,而是「有多少比例的人绩效 ≤ 当前人」
- 如果想让相同绩效的人也体现细微差异(比如按入职时间二次排序),可加
ORDER BY performance DESC, hire_date ASC,但需确认业务是否允许人为打破并列
和 NTILE 混用时要注意分桶逻辑冲突
有人会想:先用 CUME_DIST 算出比例,再用 NTILE(4) 分四档。但这两者逻辑不同:CUME_DIST 是连续比例,NTILE 是强行等分行数(可能把相邻绩效硬拆到不同档)。
例如 7 行数据,NTILE(4) 会分出大小为 2,2,2,1 的桶,而 CUME_DIST ≤ 0.25 可能只命中 1 行(因为 1/7 ≈ 0.14)。二者不能直接映射。
实操建议:
- 若目标是「前 25% 绩效员工」,直接用
CUME_DIST() OVER (ORDER BY performance DESC) - 若目标是「均分为四组」,用
NTILE(4) OVER (ORDER BY performance DESC),别混用 - 混合使用时,务必用子查询或 CTE 先固化
CUME_DIST结果,避免窗口函数嵌套引发不可预期行为
真正容易被忽略的是:累积分布对空值(NULL)的处理方式——默认 NULLS LAST(PostgreSQL/Oracle),但 MySQL 8.0 默认 NULLS FIRST。如果绩效字段有空值,务必显式声明 ORDER BY performance DESC NULLS LAST,否则空值可能挤占最高档位。

















