累计百分比的核心公式是“当前累计和 ÷ 总和”,分子用SUM(value) OVER (ORDER BY key ROWS UNBOUNDED PRECEDING),分母必须为静态总和,即SUM(value) OVER ()或SUM(value) OVER (PARTITION BY group_col),严禁分子分母同构,须防NULL、除零及整数截断。

累计百分比的核心公式必须用 SUM() OVER() 除以总和
直接用 SUM(value) OVER (ORDER BY key) 只能得到累计和,不是百分比。必须显式除以全表或分区的总和——这个分母不能用 COUNT(*) 或硬编码,得靠另一个窗口函数或子查询获取。常见错误是写成 SUM(value) OVER (ORDER BY key) / SUM(value),这会触发聚合错误,因为 SUM(value) 缺少上下文。
正确做法是:分子用有序累计和,分母用无序总和(即不带 ORDER BY 的 SUM() OVER())。两者必须在同一作用域内可比,否则结果会错位。
- 分母必须是
SUM(value) OVER ()(全表)或SUM(value) OVER (PARTITION BY group_col)(按组),不能省略OVER - 分子必须带
ORDER BY保证累计顺序,且排序字段需唯一或配合ROWS UNBOUNDED PRECEDING显式声明范围 - 如果 value 为 NULL,
SUM()会自动忽略,但除法时要小心分母为 0,建议加NULLIF(..., 0)
ORDER BY 字段重复时累计值可能“跳变”
当 ORDER BY 的字段存在重复值(比如多个订单同一天),SQL Server 默认按任意顺序处理这些行,导致累计和在相同排序键处不稳定——同一语句多次执行可能给出不同累计值,进而让百分比抖动。这不是 bug,是窗口函数定义决定的。
解决方法是补全排序键,确保唯一性:
- 优先加主键或唯一标识列,如
ORDER BY order_date, order_id - 若无自然唯一键,可用
ORDER BY order_date, ROW_NUMBER() OVER (ORDER BY order_date, (SELECT 0))强制稳定顺序(不推荐生产环境) - 避免只用
ORDER BY category这类高重复度字段做累计依据
PERCENT_RANK() 和 CUME_DIST() 不是你要的“累计百分比”
很多人搜“累计百分比”时误用 PERCENT_RANK() 或 CUME_DIST(),它们计算的是**当前行在排序中的相对位置**,不是“从第一行累加到当前行占总体的比例”。例如 5 行数据中第 3 行的 CUME_DIST() 是 0.6,但若第 1–3 行 value 总和只占全量 20%,那它就不是你想要的业务含义。
真正符合业务口径的累计百分比一定是:SUM(value) OVER (ORDER BY x ROWS UNBOUNDED PRECEDING) * 100.0 / SUM(value) OVER ()
-
PERCENT_RANK()返回 [0,1) 区间,首行恒为 0;CUME_DIST()返回 (0,1],末行恒为 1 - 二者都不感知 value 的数值大小,只看行序,无法反映金额、数量等维度的累计占比
- 若需求真是“前 N 名占多少”,才考虑这两个函数;否则一律用 SUM 累计 + 总和相除
大数据量下性能敏感点:避免重复扫描
写成子查询先算总和(如 (SELECT SUM(value) FROM t))看起来直观,但在大表上会导致外层每行都触发一次子查询求值,实际执行计划里常出现 Nested Loops + Compute Scalar,性能断崖式下降。
窗口函数方案虽多扫一遍,但 SQL Server 优化器能将其合并为单次扫描 + Stream Aggregate,吞吐更稳:
SELECT
id,
value,
SUM(value) OVER (ORDER BY id ROWS UNBOUNDED PRECEDING) * 100.0 /
NULLIF(SUM(value) OVER (), 0) AS cum_pct
FROM sales;- 确认执行计划中
SUM() OVER ()和SUM() OVER (ORDER BY ...)是否共用同一个 Segment Iterator - 如果表有合适索引(如
INDEX IX_on_id INCLUDE (value)),累计计算能走索引有序扫描,避免 Sort - 分区表场景下,用
PARTITION BY region替代全表总和,可大幅减少中间结果集
分母是否该用全表总和还是动态分区总和,取决于业务定义——这点容易被忽略,但改起来成本极高,上线前务必和业务方对齐口径。

















