mysql 8.0+ 最直接的分组累计求和写法是 sum(value) over (partition by group_col order by order_col),缺一不可:partition by 划分组,order by 确保组内逐行累加;漏掉 order by 将返回组内总和而非累计值。

MySQL 8.0+ 用 SUM() 窗口函数最直接
MySQL 8.0 开始原生支持窗口函数,SUM() 配合 OVER() 就能按分组做累计求和,不用子查询或变量。关键在 ORDER BY 和 PARTITION BY 的组合:
-
PARTITION BY定义分组边界(比如按category分组) -
ORDER BY在组内定义累计顺序(比如按date或id排序) - 漏掉
ORDER BY会导致整组总和,不是“累计”
示例:按品类统计每日销售额的累计值
SELECT category, sale_date, amount, SUM(amount) OVER (PARTITION BY category ORDER BY sale_date) AS cum_amount FROM sales;
PostgreSQL / SQL Server 同样适用 SUM() OVER
这些数据库对窗口函数支持更早、更稳定,语法一致,但要注意:ORDER BY 子句不可省略,否则会报错或结果不符合预期。
- PostgreSQL 允许
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW显式声明范围(默认就是这个),但一般不用写 - SQL Server 中如果排序字段有重复值,累计结果可能“跳跃”——因为相同排序值被当作同一行处理,建议加唯一键(如
id)辅助排序 - 避免在
OVER()里混用PARTITION BY和全局ORDER BY,逻辑容易错乱
旧版 MySQL(5.7 及以下)只能靠变量模拟
没有窗口函数时,必须用用户变量 + 正确排序来模拟累计逻辑,风险高、不可靠:
- 变量赋值顺序依赖
ORDER BY,但 MySQL 不保证变量计算与排序严格同步,尤其在有JOIN或复杂条件时容易出错 - 必须用派生表或 CTE 包一层,否则变量可能被优化器重排执行顺序
- 不能在同一个语句中既读又写同一变量(比如
@sum := @sum + amount在某些版本会返回NULL)
勉强可用的写法(仅限简单场景):
SELECT
category, sale_date, amount,
@sum := IF(@prev = category, @sum + amount, amount) AS cum_amount,
@prev := category
FROM (SELECT * FROM sales ORDER BY category, sale_date) t,
(SELECT @sum := 0, @prev := '') AS vars;
GROUP BY 后无法直接用 SUM() 得到累计值
这是常见误解:GROUP BY 是聚合,输出的是每组一个标量;累计总和是逐行计算的,必须保留下原始行粒度。
- 如果先
GROUP BY再想累计,得把结果再套一层查询,用窗口函数或变量处理 - 误写成
SUM(SUM(amount)) OVER (...)会报错:聚合函数不能嵌套在窗口函数里 - 想按月汇总后再累计?先用
GROUP BY YEAR(date), MONTH(date)聚合,再在外层查中用SUM() OVER
真正要小心的是排序稳定性——哪怕语法写对了,如果排序字段不唯一,不同数据库或版本下累计结果可能不一致。










