CUME_DIST返回“≤当前值的行数÷分组总行数”,结果范围(0,1]且最小值>0;PERCENT_RANK按(rank−1)/(total−1)计算,首行为0、末行未必为1;前者基于值计数、重复值结果相同,后者基于排名、对重复值处理更松散。

什么是 CUME_DIST,它和 PERCENT_RANK 有什么区别?
CUME_DIST 返回当前行在分组内的累积分布值,即“小于等于当前值的行数 ÷ 分组总行数”,结果范围是 (0, 1],且最小值一定大于 0(因为至少包含自身)。它不依赖排序位置差值,而是严格按值计数——相同值会得到相同结果,且不会出现 0。
对比 PERCENT_RANK:后者是 (rank - 1) / (total_rows - 1),首行必为 0,相同值共享 rank 但结果仍可能不同;而 CUME_DIST 对重复值更“保守”,比如三行并列第一,CUME_DIST 都返回 1.0,PERCENT_RANK 则返回 0.0。
怎么写 CUME_DIST 的基本语法?ORDER BY 是必须的吗?
CUME_DIST() 必须配合 OVER 子句,且 ORDER BY 在窗口定义中不可省略——没有排序就无法定义“累积到哪”,否则报错 ERROR: window function CUME_DIST requires an ORDER BY clause。
常见写法:
SELECT score, CUME_DIST() OVER (ORDER BY score) AS cume_dist FROM exam_scores;
若需分区计算(如按科目分别统计),加 PARTITION BY:
SELECT subject, score, CUME_DIST() OVER (PARTITION BY subject ORDER BY score) AS cume_dist FROM exam_scores;
-
ORDER BY支持多列,例如ORDER BY score DESC, id ASC - NULL 默认排在最前(
NULLS FIRST),若想让 NULL 排最后,显式写ORDER BY score NULLS LAST - 不能在
WHERE或GROUP BY中直接引用CUME_DIST()别名,得用子查询或 CTE
遇到结果全是 1.0 或小数精度异常怎么办?
常见误判:看到所有 CUME_DIST 值都是 1.0,第一反应是函数写错了。其实更可能是数据本身全相同——比如整列 score 都是 85,那每行都满足“≤85”的条件,占比就是 100%。
另一个坑是浮点数比较导致重复值未被识别:数据库可能把 92.0 和 92.00 视为不同值(取决于类型定义),建议统一 cast 为 DECIMAL 或使用 ROUND(score, 2) 再排序。
- PostgreSQL 和 SQL Server 行为一致;MySQL 8.0+ 支持,但旧版本不支持
- 结果默认是 double 类型,显示可能截断(如 0.3333333333333333 → 0.333),需要时用
ROUND(cume_dist, 4) - 如果
ORDER BY列有大量重复值,CUME_DIST会跳变(如 3 行同值后突然从 0.6→1.0),这是正常行为,不是 bug
能不能用 CUME_DIST 实现“前 10%”筛选?
可以,但要注意边界处理:CUME_DIST 找的是“分布值 ≤ 10%”的行,对应的是“≤该分数的人占总数 ≤10%”,实际人数可能少于总行数的 10%(尤其当存在重复值时)。
更稳妥的做法是结合 ROW_NUMBER 或 NTILE:
SELECT * FROM (
SELECT *,
NTILE(10) OVER (ORDER BY score DESC) AS decile
FROM exam_scores
) t WHERE decile = 1;-
CUME_DIST按升序计算(默认),要找高分前 10%,得用ORDER BY score DESC - 如果总行数是 99,
CUME_DIST 可能只返回 9 行;而 <code>NTILE(10)强制分 10 组,第 1 组固定约 10 行 - 别直接用
CUME_DIST = 0.1做等值判断——浮点误差和离散分布会让它几乎永远为 false
真正难的是理解“累积分布”本质:它描述的是值的位置密度,不是行号比例。值越密集,CUME_DIST 跳跃越小;值越稀疏,跳跃越大。这点容易被忽略,却直接影响筛选逻辑是否符合业务预期。

















