CUME_DIST()是累积分布函数,计算“小于等于当前值的行数÷总行数”,结果范围为(0,1];它不是百分比排名,但配合PARTITION BY和ORDER BY可在分组内实现占比分析,相同值共享同一累积值。

什么是 CUME_DIST()?它和百分比排名有什么关系
CUME_DIST() 不是“百分比排名”,而是**累积分布值**:对当前行按排序顺序,返回「小于等于当前值的行数 ÷ 总行数」。结果范围是 (0, 1],且相同值会得到相同的累积分布值——这恰好能用来实现「分组内百分比排名」,但必须配合 PARTITION BY 使用,否则就是全表统计。
常见误解是把它当 PERCENT_RANK() 或 RANK() 用,但三者逻辑不同:CUME_DIST() 永远不会返回 0,最小值也对应「该值首次出现位置占总体的比例」;而 PERCENT_RANK() 最小值固定为 0。
如何在分组内正确使用 CUME_DIST() 计算百分比排名
关键在于窗口定义必须同时指定 PARTITION BY 和 ORDER BY,否则结果毫无业务意义。
-
PARTITION BY决定“分组范围”(比如按department分组) -
ORDER BY决定“排序依据”(比如按salary DESC) - 函数本身不接受参数,只依赖窗口定义
示例:计算各部门员工薪资的累积分布百分比
SELECT name, department, salary, ROUND(CUME_DIST() OVER (PARTITION BY department ORDER BY salary DESC), 4) AS cume_pct FROM employees;
注意:ROUND(..., 4) 是为了可读性,实际业务中是否四舍五入需按需决定。
为什么结果不是整数百分比?怎么转成「XX%」格式
CUME_DIST() 返回的是小数(如 0.3333),不是带 % 符号的字符串。直接乘 100 再加 '%' 是最常用做法,但要注意数据库类型兼容性:
- PostgreSQL / SQL Server:支持直接拼接
CONCAT(ROUND(cume_pct * 100, 2), '%') - MySQL 5.7+:可用
CONCAT(ROUND(cume_pct * 100, 2), '%'),但注意ROUND()对浮点精度的影响 - BigQuery:推荐用
FORMAT('%.2f%%', cume_pct * 100)避免隐式转换问题
别写成 CAST(cume_pct * 100 AS INT) || '%'——会丢失小数位,导致多个不同名次显示为相同百分比。
CUME_DIST() 的边界行为和易错点
最容易出错的是忽略 NULL 值处理和排序方向一致性:
- 默认情况下,
NULLS FIRST还是NULLS LAST因数据库而异(PostgreSQL 默认NULLS FIRST,Oracle/SQL Server 默认NULLS LAST),可能导致同一条语句在不同环境结果不一致 - 如果
ORDER BY字段含 NULL,且未显式声明NULLS LAST,高薪组可能被排在最前,拉低整体累积比例 - 分组字段(
PARTITION BY)若存在 NULL,会被视为独立一组——例如department IS NULL的员工自成一组参与计算,常被忽略 -
CUME_DIST()无法跳过重复值(不像DENSE_RANK()可控),相同 salary 必然共享同一累积值,这是设计使然,不是 bug
真正在意「第几名」而非「占比多少」时,应该换用 ROW_NUMBER() 或 RANK(),CUME_DIST() 的用途很窄:只适合回答“我的成绩超过了百分之几的人”。

















