CUME_DIST()返回「小于等于当前行值的行数占分区总行数的比例」,结果范围为(0,1],相同值行共享同一值;必须配合ORDER BY使用,不接受参数,且对NULL敏感。

什么是 CUME_DIST() 的返回值含义?
CUME_DIST() 返回的是「小于等于当前行值的行数占总行数的比例」,结果范围是 (0, 1],且相同值的行会得到相同的累积分布值。它不是按顺序编号再除以总数,而是基于排序后的位置和重复值做分组计算。
比如 5 行数据中,有 2 行并列第 1,那么这两行的 CUME_DIST() 都是 2/5 = 0.4;下一行哪怕值更大,也从第 3 个位置开始算,分子是「≤它的所有行数」。
- 必须配合
ORDER BY使用,否则报错(ERROR: window function requires an ordering clause) - 不支持
PARTITION BY时省略ORDER BY—— 即使只有一列也要显式写 - 结果是
double precision类型,不要用==做浮点精确比较
如何在 PostgreSQL 中正确调用 CUME_DIST()?
PostgreSQL 支持标准语法,但要注意窗口定义必须完整:
SELECT score, CUME_DIST() OVER (ORDER BY score) AS cume_dist FROM exam_scores;
常见错误包括:
- 漏掉括号:
CUME_DIST OVER→ 报错,必须是CUME_DIST() - 写成
CUME_DIST(score)→ 函数不接受参数,会提示function cume_dist(numeric) does not exist - 在子查询里没给窗口别名,又在外层引用
cume_dist→ 别名可用,但需确保 SELECT 列中已定义
MySQL 8.0+ 怎么用 CUME_DIST()?
MySQL 8.0 起支持该函数,语法一致,但对 NULL 值的处理更敏感:默认把 NULL 当作最小值排在最前。如果业务要求 NULL 排最后,得手动处理:
SELECT score, CUME_DIST() OVER (ORDER BY score IS NULL, score) AS cume_dist FROM exam_scores;
这个技巧利用了 score IS NULL 返回 0/1,让非 NULL 先排,再按 score 排序。
- MySQL 不支持
RANGE窗口帧,CUME_DIST()固定作用于整个分区(即ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) - 若用
PARTITION BY subject,每个科目单独计算累积分布,注意跨分区不可比
为什么 CUME_DIST() 和 PERCENT_RANK() 结果不同?
两者常被混淆,但逻辑完全不同:PERCENT_RANK() 是 (rank - 1) / (总行数 - 1),首行永远是 0,末行永远是 1;而 CUME_DIST() 是 ≤当前值的行数 / 总行数,首行至少是 1/n,末行一定是 1。
例如 4 行值为 [10, 20, 20, 30]:
-
CUME_DIST()得到 [0.25, 0.5, 0.5, 1.0] -
PERCENT_RANK()得到 [0.0, 0.33, 0.33, 1.0]
选哪个取决于业务定义——要「占比多少人≤我」就用 CUME_DIST();要「我在排名中的相对位置(归一化到 0–1)」才考虑 PERCENT_RANK()。
真正容易被忽略的是:当数据量小或存在大量重复值时,CUME_DIST() 的阶梯状跳跃会非常明显,直接用于分段阈值(比如取 top 10%)可能卡在某个重复值上,导致实际入选人数远超预期。

















