CUME_DIST()返回「小于等于当前行值的行数占分区总行数的比例」,结果范围为(0,1],最小值大于0、最大值恒为1;相同值行获得相同比例,必须配合ORDER BY使用,不可省略。

什么是 CUME_DIST() 的返回值?
CUME_DIST() 返回的是「小于等于当前行值的行数占总行数的比例」,结果范围是 (0, 1],不是从 0 开始。它按 ORDER BY 列升序计算,相同值共享同一分布值(即并列排名会得到相同结果)。
- 值不是“当前行位置 / 总行数”,而是「≤ 当前行值的行数 / 总行数」
- 数据必须显式指定
ORDER BY,否则报错:Window function CUME_DIST requires an ORDER BY clause - 不支持
PARTITION BY后省略ORDER BY,哪怕只分组不排序也不行
CUME_DIST() 和 PERCENT_RANK() 有什么关键区别?
两者都算相对位置,但定义不同:
-
CUME_DIST():包含当前值的所有行占比(含并列),最小值 > 0,最大值 = 1 -
PERCENT_RANK():(rank − 1) / (总行数 − 1),最小值 = 0,最大值
SELECT score,
CUME_DIST() OVER (ORDER BY score) AS cume_dist,
PERCENT_RANK() OVER (ORDER BY score) AS pctrank
FROM (VALUES (70), (80), (80), (95)) t(score);
结果中两个 80 行的 cume_dist 都是 0.75(3 行 ≤ 80,共 4 行),而 pctrank 都是 0.333…(rank=2,(2−1)/(4−1))
如何避免常见错误:NULL 值和排序方向的影响
CUME_DIST() 默认把 NULL 当作最小值(即排在最前),所以如果字段含 NULL,它们会拉低开头的累计比例,容易误读:
- 若想忽略
NULL,先用WHERE score IS NOT NULL过滤,或在ORDER BY中显式控制:ORDER BY score NULLS LAST - 降序使用时,语义变为「≥ 当前行值的行占比」,但函数名不变,需靠注释或别名提醒自己:
CUME_DIST() OVER (ORDER BY score DESC) - 在 PostgreSQL 中支持
NULLS FIRST/LAST;MySQL 8.0+ 和 SQL Server 也支持,但 SQLite 不支持该语法
实际业务场景中怎么用才不翻车?
累计百分比常用于划定 Top N% 用户、分位数切片(如 P90)、或质量报告中的缺陷累积率:
- 计算 P95 分数时,不能直接找
cume_dist >= 0.95的第一行 —— 因为它是「≤ 当前值」的占比,应取满足条件的最小分数:MIN(score) FILTER (WHERE cume_dist >= 0.95)(PostgreSQL)或用子查询 +ORDER BY cume_dist LIMIT 1 - 多字段排序时,
ORDER BY score, id可打破并列,让每行CUME_DIST()唯一,但业务意义可能失真(比如同分用户被强行区分) - 如果表很大且只查某一分位,用
APPROX_PERCENTILE()(如 Trino、BigQuery)通常更快,CUME_DIST()是精确全量窗口计算,IO 和内存开销高
真实需求里,多数人要的不是“每个分数的累计占比”,而是“多少分能进前 10%”——这时候 CUME_DIST() 是中间步骤,不是终点。别忘了它不自动帮你做阈值提取。

















