SUM() OVER() 是最简洁可靠的累计求和方案,需显式指定 ORDER BY,推荐用 ROWS 框架避免重复值问题;MySQL 5.7- 需变量模拟但不可靠,应升级至 8.0+。

用窗口函数 SUM() OVER() 是最简洁可靠的方案
绝大多数现代 SQL 引擎(PostgreSQL、SQL Server、Oracle、Snowflake、BigQuery、MySQL 8.0+)都支持窗口函数,SUM() OVER() 是实现累计求和最直接、语义最清晰的方式。它不需要自连接、子查询或变量,性能好且可读性强。
关键点在于正确指定窗口的排序逻辑:ORDER BY 必须存在,否则累计行为无定义;PARTITION BY 可选,用于分组内独立累计(比如按用户、按月份)。
-
OVER (ORDER BY date):按日期全局累计 -
OVER (PARTITION BY user_id ORDER BY created_at):每个用户按创建时间单独累计 - 漏写
ORDER BY会导致结果非确定性(尤其在 PostgreSQL 中可能报错,在 MySQL 中可能返回任意顺序的“累计”)
MySQL 5.7 或更老版本只能靠变量模拟
这些版本不支持窗口函数,需用用户变量 + ORDER BY 强制执行顺序来模拟。但要注意:MySQL 不保证 SELECT 中变量赋值的执行顺序,该写法属于“依赖未定义行为”,仅在特定版本+严格排序下偶然有效。
示例(风险高,仅作兼容参考):
SELECT id, amount, @running_total := @running_total + amount AS running_total FROM orders, (SELECT @running_total := 0) r ORDER BY date;
- 必须用
ORDER BY显式排序,且不能有LIMIT在变量计算之后 - 不能在同一个
SELECT中多次引用同一变量(如@r := @r + x, @r),行为不可靠 - 升级到 MySQL 8.0+ 后应立即改用
SUM() OVER()
SUM() OVER() 的常见误用与边界
看似简单,但几个细节容易导致结果不符合预期:
- 重复值 + 未指定
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:默认框架是RANGE BETWEEN ...,当ORDER BY列有重复值时,会把同值所有行“捆在一起”累计(例如同一天多笔订单,当天总额会一次性加总到第一行)。解决方法是显式写ROWS框架或确保ORDER BY唯一(如ORDER BY date, id) - NULL 值参与计算:
SUM()会跳过 NULL,但若首行amount就是 NULL,则running_total为 NULL,后续非 NULL 行仍能正常累加——这点常被忽略 - 浮点数精度:累计过程中误差会累积,金融场景建议用
DECIMAL类型字段
替代方案:自连接或相关子查询太重,不推荐
例如用 (SELECT SUM(t2.amount) FROM t t2 WHERE t2.date 实现,逻辑直观但复杂度是 O(n²),数据量稍大(几千行以上)就明显变慢,且难以扩展分区逻辑。
还有人尝试用 CTE 递归,但不仅写法冗长,而且 PostgreSQL/SQL Server 对递归深度有限制,MySQL 根本不支持递归 CTE(直到 8.0),纯属绕远路。
真正需要关注的只有两件事:是否用对了窗口函数,以及 ORDER BY 是否满足业务顺序要求。其余都是次要权衡。










