CUME_DIST返回当前行值在分组内“≤该值”的行数占比,范围(0,1]且最小值为1/N;PERCENT_RANK按(rank-1)/(total-1)计算,首行为0、末行未必为1,二者计算逻辑和业务语义不同。

什么是CUME_DIST,它和PERCENT_RANK有什么区别?
CUME_DIST 返回当前行在分组内的相对位置:等于或小于当前值的行数 ÷ 分组总行数。结果范围是 (0, 1],最小值一定是 1/N(N为分组行数),最大值恒为 1。
它和 PERCENT_RANK 不同:PERCENT_RANK 是(当前行排名 - 1)÷(总行数 - 1),所以首行结果为 0;而 CUME_DIST 首行至少是 1/N,更贴近“占比”直觉。
常见错误是误以为两者可互换——比如按销售额排序后想看“有多少比例的订单 ≤ 当前订单”,必须用 CUME_DIST,用 PERCENT_RANK 会低估(尤其小数据集)。
基本语法和ORDER BY必须存在
CUME_DIST() 是窗口函数,必须搭配 OVER 子句,且 ORDER BY 不可省略。不写 ORDER BY 会报错,例如:
SELECT order_id, amount, CUME_DIST() OVER () FROM orders;
这条语句在 PostgreSQL / SQL Server / Oracle 中都会报错,提示缺少排序依据。
正确写法:
SELECT order_id, amount, CUME_DIST() OVER (ORDER BY amount) AS cume_dist FROM orders;
注意:如果 amount 有重复值,CUME_DIST 对所有相同值返回相同结果(即并列处理),这点和 RANK 类似,但计算逻辑不同。
配合PARTITION BY做分组内累计分布
实际业务中常需“每个部门内薪资的累计分布”,这时用 PARTITION BY:
SELECT dept, salary, CUME_DIST() OVER (PARTITION BY dept ORDER BY salary) AS cume_dist FROM employees;
关键点:
-
PARTITION BY dept把数据按部门切片,各组独立计数 - 同一部门内,
CUME_DIST结果之和 ≠ 1(它是每行的占比,不是概率密度) - 若某部门只有 1 行,该行
CUME_DIST恒为 1.0
容易踩的坑:误把 PARTITION BY 放在 WHERE 后面过滤——窗口函数在 WHERE 之后执行,所以分区基于过滤后的结果,不是原始表。
NULL值怎么处理?不同数据库行为不一致
CUME_DIST 对 NULL 的排序默认依赖数据库:
- PostgreSQL / SQL Server:默认
NULLS LAST,即NULL排在末尾,不参与“≤ 当前值”的计数(除非当前行就是NULL) - Oracle:默认
NULLS FIRST
如果你显式写了 ORDER BY amount NULLS FIRST,那么所有 NULL 行的 CUME_DIST 值相同,且等于(NULL 行数)÷ 总行数。
建议做法:业务上明确 NULL 是否应参与分布计算。若不应参与,先用 WHERE amount IS NOT NULL 过滤;若应参与,统一指定 NULLS FIRST 或 NULLS LAST,避免跨库迁移时结果漂移。
CUME_DIST 看似简单,但分区边界、NULL 排序、重复值聚合这三点一旦没对齐,输出就和预期差一个数量级。写完记得用小样本手算验证首尾行。

















