cume_dist()直接返回≤当前值的行数占窗口总行数的比例,结果为0~1浮点数,必须配合order by使用,不可用于where/having,需格式化展示。

CUME_DIST() 不是“算百分比”的函数,而是直接返回累积分布值——它天然就是百分比的数学表达,只是没加%、没四舍五入而已。
为什么 CUME_DIST() 返回的就是“≤当前值的比例”
CUME_DIST() 的定义就是:(小于等于当前行值的行数) ÷ (当前窗口总行数)。这个比值本身就是 0~1 区间的浮点数,例如 0.75 就代表“75% 的数据 ≤ 当前行的值”。它不依赖任何插值或估算,也不需要你手动写 COUNT(IF(...)) / COUNT(*) 这类易错聚合嵌套。
- 结果范围严格为
(0, 1]:最小值 > 0(至少含自己),最大值恒为 1(最高值必然覆盖全部) - 相同值自动归并:三个 85 分在 5 行中 → 全部返回
3/5 = 0.6,语义清晰,无需额外去重或分组处理 - 不假设数据连续或正态分布:不管销售额是 100/100/100/9999 还是均匀分布,计算逻辑完全一致
CUME_DIST() 必须带 ORDER BY,否则直接报错
MySQL 8.0+、PostgreSQL、SQL Server 都强制要求 ORDER BY 子句,否则抛出类似 ERROR 3589 (HY000): Window 'w' requires an ORDER BY clause 的错误。这不是可选项,是语义前提——没有排序,“≤当前值”就无从定义。
- 漏写
ORDER BY:查询直接失败,不会静默返回错误结果 - 只写
PARTITION BY不写ORDER BY:同样报错,PARTITION BY只负责切分窗口,不提供顺序 - 排序方向影响业务含义:按
score DESC是“高分占比”,按score ASC是“低分占比”,别反着用
不能在 WHERE 或 ON 中直接用 CUME_DIST()
WHERE CUME_DIST() OVER (...) > 0.9 是非法语法,所有主流数据库都会拒绝执行。窗口函数不能出现在过滤子句中,因为其计算依赖于完整窗口结果,而 WHERE 在窗口计算前就已执行。
- 正确做法是套一层子查询或 CTE:
SELECT * FROM (SELECT *, CUME_DIST() OVER (...) AS p FROM t) t2 WHERE p >= 0.9 - 别试图用
HAVING替代——它只对 GROUP BY 有效,和窗口无关 - 应用层过滤更安全:把
cume_dist字段查出来,在代码里判断是否 ≥ 0.95,避免 SQL 层嵌套过深
展示时必须格式化,否则小数精度会误导人
原始返回值是 DOUBLE,比如 0.9999999999999999,直接拼 '%' 会显示为 "99.99999999999999%",报表里根本没法读。
- 推荐写法:
CONCAT(ROUND(cume_dist * 100, 2), '%') - 禁用
CAST(cume_dist * 100 AS SIGNED):会截断小数,导致 0.94 和 0.96 全变成94% - 大数据量下 ROUND() 可能因浮点误差失准,若要求严格,建议在应用层转
DECIMAL或用字符串格式化
真正容易被忽略的是:CUME_DIST() 的“分母”永远是当前窗口的总行数,包括 NULL;而业务上常希望排除空值——但 MySQL 8.0+ 不支持 NULLS LAST,所以得先用 WHERE score IS NOT NULL 过滤,否则第一个非空值的累积比例可能虚高。











