必须用sum() over(partition by ... order by ...)实现累加,因group by会压缩行数丢失逐行结构,而缺order by时窗口函数仅返回分组总和而非累计值,order by定义“当前行”逻辑顺序,是累加成立的前提。

不能用 GROUP BY 后再套 SUM() 实现累加,因为那会把行压成一条,原始顺序和逐行结构全丢了——你要的“截至当前行的累计值”,必须靠窗口函数。
为什么 SUM() OVER 必须带 ORDER BY
没 ORDER BY 的 SUM() OVER(PARTITION BY ...) 看似能跑,实则每行都显示该分组总和,不是累计。数据库不知道“当前行”是哪一行,只能把整组当一个单元处理。
-
SUM(amount) OVER (PARTITION BY user_id ORDER BY create_time)→ 每个用户内部按时间递增累加 -
SUM(amount) OVER (PARTITION BY user_id)→ 每行都是该用户的总金额,重复值 - MySQL 8.0+、PostgreSQL、SQL Server 都要求显式
ORDER BY,否则报错或结果不可靠
PARTITION BY 和 ORDER BY 怎么配才不出错
分组边界和累加顺序必须同时明确,且排序字段得有业务意义、尽量唯一。
- 时间字段重复很常见:用
ORDER BY create_time, id补主键兜底,避免同秒多笔导致累计跳变 - NULL 值默认排序位置不一致:MySQL 把
NULL排最前,PostgreSQL 排最后;统一用COALESCE(create_time, '1970-01-01')处理 - 别用无序字段排序:比如
ORDER BY name,重名时顺序不确定,结果不可复现 - 想每月重置累计?把年月放进
PARTITION BY:PARTITION BY user_id, YEAR(create_time), MONTH(create_time)
老版本 MySQL(5.7 及之前)怎么临时应付
窗口函数不支持,变量模拟是唯一路径,但极不稳定,仅限小数据量、单线程场景下救急。
- 必须显式初始化:
(SELECT @sum := 0) AS _init放在FROM子句里 - 主表必须提前排序:
FROM (SELECT * FROM orders ORDER BY user_id, create_time) t,否则变量执行顺序乱 - 并发查询下变量值可能污染,视图/存储过程里完全不可用
- 强烈建议升级到 MySQL 8.0+,或换用 PostgreSQL / DuckDB 等原生支持窗口函数的引擎
真正容易被忽略的是:累计不是数学运算,而是逻辑路径——ORDER BY 定义了“哪一行该先加”,哪怕业务上觉得“顺序无所谓”,数据库也必须知道。漏掉它,或者选错字段,结果就不是累加,只是巧合碰对了数字。











