MySQL 8.0 及以上版本支持 SUM() 窗口函数,常见写法为 SUM(column) OVER (ORDER BY key_column)。其中,ORDER BY 是实现累计求和的关键条件,不能省略;PARTITION BY 则可根据业务场景选择是否使用。该函数默认采用 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 作为窗口帧,因此非常适合用于计算累计值、累计金额、累计销量等结果。使用时还需要重点关注 NULL 值处理、排序字段是否唯一,以及索引是否合理,以保证查询结果准确且性能稳定。

MySQL SUM窗口函数的基本写法
需要特别注意,只有 MySQL 8.0 及以上版本才支持 SUM() 窗口函数。如果数据库版本低于 8.0,执行时通常会直接报错,例如提示 FUNCTION xxx does not exist。另外,使用 MySQL SUM 窗口函数计算累计值时,ORDER BY 子句必须存在。原因很简单:没有排序顺序,就无法定义“从前到后”的累计逻辑,同时系统也会报错,例如 This function requires an ORDER BY clause。
它的基本语法结构是:SUM(column) OVER (ORDER BY key_column),其中 key_column 一般使用时间字段、主键 ID,或其他能够明确表示先后顺序的排序字段。
- 不写
PARTITION BY:表示整张表按照排序字段顺序做累计求和 - 添加
PARTITION BY category:表示在每个分组内分别累计,例如按用户、按月份、按分类统计 - 不能只写
OVER ()—— 对于累计求和场景来说,空窗口定义是无效的,也不符合实际计算逻辑
累计值 vs 当前行求和的常见误解
很多开发者会把 SUM(amount) OVER (ORDER BY id) 理解为“从第一行一直累加到当前行”,这个理解本身没有问题;但更容易被忽视的是,真正决定这种累计效果的,是默认窗口帧 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。
如果你显式写成 SUM(amount) OVER (ORDER BY id ROWS BETWEEN CURRENT ROW AND CURRENT ROW),那么结果就不再是累计值,而是每一行只计算当前行本身的值。
- 默认 frame 通常就能满足绝大多数 MySQL 累计求和场景,写法简洁且稳定
- 如果需要“截至上一行”的累计结果,也就是不包含当前行,可以手动指定
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING - MySQL 不支持在
RANGE帧中直接使用INTERVAL,例如RANGE BETWEEN INTERVAL 7 DAY PRECEDING AND CURRENT ROW这类写法不可用
与 GROUP BY 混用时的典型错误
在实际 SQL 开发中,很多人希望先分组再做累计,比如“先统计每个用户每天的销售额,再计算用户维度下的累计销售额”。这时候要特别小心,不能直接在 GROUP BY user_id, sale_date 之后再套窗口函数,因为 GROUP BY 会先压缩结果集,窗口函数就无法基于原始逐行数据执行累计计算。
更合理的做法通常是:先使用窗口函数完成累计值计算,如果后续确实还需要聚合,再放到外层查询中处理。很多情况下,其实根本不需要 GROUP BY,直接通过 PARTITION BY user_id ORDER BY sale_date 就可以实现分组内累计求和。
- 错误示例:
SELECT user_id, SUM(sale) AS daily_total, SUM(sale) OVER (ORDER BY sale_date) FROM t GROUP BY user_id, sale_date→ 可能报错,或者得到不符合预期的结果 - 正确示例:
SELECT user_id, sale_date, sale, SUM(sale) OVER (PARTITION BY user_id ORDER BY sale_date) AS cumsum FROM t - 注意:如果
sale_date存在重复值,建议补充二级排序字段,例如id,否则累计顺序可能不稳定,进而影响最终结果
性能和 NULL 处理的实际影响
SUM() 窗口函数会自动忽略 NULL 值,这一点与普通聚合函数 SUM() 的行为一致。不过也要注意,如果排序字段本身为 NULL,在升序排序 ASC 的情况下,MySQL 会把这些记录排在前面,这可能让累计值的起始位置出现偏差。
从性能优化角度看,MySQL 窗口函数高度依赖排序过程。数据量较大时,ORDER BY 相关字段最好建立合适索引,否则执行计划中很容易出现 Using filesort,导致磁盘 IO 和 CPU 消耗明显上升,进而影响 SQL 查询效率。
- 推荐建立组合索引:
(user_id, sale_date, sale),适用于PARTITION BY user_id ORDER BY sale_date这类典型累计统计场景 - 可以使用
EXPLAIN ANALYZE检查执行计划,确认是否走索引扫描,而不是全表扫描后再排序 - 尽量避免在窗口函数中嵌套复杂子查询或高成本表达式,例如
SUM(ROUND(price * tax_rate)) OVER (...),否则通常会明显拖慢执行速度
在真实业务报表中,累计值往往需要和原始明细数据一起展示,因此 MySQL SUM 窗口函数通常会配合 SELECT * 或明细字段同时输出。而实践中最容易被忽略的两个问题,正是排序字段的唯一性和索引覆盖能力——一旦这两点处理不到位,就可能出现结果时对时错、性能忽高忽低的情况,后续排查也会非常耗时。
