CUME_DIST()是窗口函数,返回≤当前值的行数占窗口总行数的比例,结果为(0,1]区间小数;必须带ORDER BY,支持PARTITION BY分组计算,需ROUND()格式化显示,不可在WHERE中直接过滤。

CUME_DIST() 不是排名函数,它返回的是“≤当前分数的行数 ÷ 当前窗口总行数”,结果直接就是百分比数值(0, 1] 区间,不用自己写 COUNT(IF()) / COUNT(*) 这类易错聚合。
必须带 ORDER BY,否则直接报错
MySQL 8.0 强制要求 CUME_DIST() 的 OVER() 子句中包含 ORDER BY,否则抛出 ERROR 3589 (HY000): Window 'w' requires an ORDER BY clause。没有排序,“≤当前值”就无定义。
- 按高分优先算分布:用
ORDER BY score DESC,这样最高分对应1.0 - 按低分优先:用
ORDER BY score ASC,最低分先累积,值更小 - 只写
PARTITION BY class而不写ORDER BY同样报错,分区不等于排序 - 多列排序合法,例如
ORDER BY score DESC, student_id ASC,用于打散并列时的顺序
分班级计算必须显式写 PARTITION BY
不加 PARTITION BY 就默认全表为一个窗口,所有学生成绩混排——某班平均分低,但因全校高分集中在另一班,该班最高分可能只拿到 0.3,看起来垫底,实际是本班第一。
- 正确写法:
CUME_DIST() OVER (PARTITION BY class ORDER BY score DESC) - 每个班级独立统计,分子分母都只算本班数据
- 如果字段含
NULL,MySQL 8.0 默认NULLS LAST,但若想排除空成绩干扰,建议提前过滤:WHERE score IS NOT NULL - 别依赖隐式行为:不同数据库对
NULL排序策略不一致(PostgreSQL 默认NULLS FIRST)
展示前必须格式化,否则小数精度会误导人
CUME_DIST() 返回的是 DOUBLE 类型,原始值如 0.9999999999999999,直接拼 '%' 会显示成 "99.99999999999999%",报表里根本没法读。
- 推荐处理:
CONCAT(ROUND(cume_dist * 100, 2), '%') - 禁用
CAST(cume_dist * 100 AS SIGNED):会砍掉小数,0.94和0.96全变94%,失去区分度 - 大数据量下
ROUND()可能受浮点误差影响,若需严格精度,建议在应用层做格式化,或先CAST(score AS DECIMAL(10,2))再参与窗口计算 - 别用
= 0.9做等值判断,改用>= 0.899或BETWEEN 0.895 AND 0.905
不能在 WHERE 或 HAVING 中直接过滤
窗口函数计算发生在 SQL 执行的后期阶段,WHERE 在它之前就已执行完毕,所以 WHERE CUME_DIST() OVER () > 0.9 是非法语法,所有主流数据库都会拒绝。
- 正确做法:套子查询或 CTE,例如
SELECT * FROM (SELECT *, CUME_DIST() OVER (ORDER BY score DESC) AS p FROM exam_results) t WHERE p >= 0.9 - 想查“前 10% 学生”,注意
0.9可能根本不出现在结果中(比如所有CUME_DIST()值是0.2、0.6、1.0),得用>= 0.9而非= 0.9 - 同一分数横跨多个班级时,
CUME_DIST()在每个班级内独立计算,但人眼容易误以为是全局比例——这点最容易被忽略


















