MySQL 5.7及更早版本需用用户变量模拟运行总计,必须先在子查询中显式排序并初始化变量,再在外层累加,避免ORDER BY与变量执行顺序不同步导致结果错误。
MySQL 5.7 及更早版本怎么写 running total
MySQL 5.7 不支持 OVER() 和窗口函数,必须用用户变量(@var)模拟。核心思路是:按业务顺序排序后,逐行累加,把上一行的累计值“传”到下一行。
常见错误是忽略 ORDER BY 在变量赋值中的执行顺序——MySQL 不保证 SELECT 中变量计算与 ORDER BY 的同步性,必须把排序逻辑显式塞进子查询或派生表里。
- 必须用子查询先排序,再在外层用变量累加,不能直接
SELECT @sum := @sum + amount FROM t ORDER BY id - 初始化变量要和查询放在同一语句中(用
CROSS JOIN或(SELECT @sum := 0) AS _),避免会话残留值干扰 - 字段别名不能和变量名同名(比如
@sum和列也叫sum),否则可能引发不可预期覆盖
SELECT id, amount, @sum := @sum + amount AS running_total FROM orders CROSS JOIN (SELECT @sum := 0) AS _ ORDER BY created_at, id;
为什么不能在 GROUP BY 后直接用变量算 running total
因为 GROUP BY 会压缩行,而变量是在结果集的每一“输出行”上执行的。如果你先 GROUP BY day 再想算每日累计,变量看到的是聚合后的行,但缺少原始时间序——它不知道“昨天”和“今天”的先后关系。
正确做法是:先按时间展开明细(哪怕只是虚拟的计数行),或用自连接/相关子查询;变量方案只适用于已严格排序的线性序列。
- 变量方案本质是“流式处理”,依赖物理返回顺序,不适用于多维度分组累计
- 如果真需要分组内 running total(如每个 user_id 下按时间累计),必须先
ORDER BY user_id, created_at,且确保 user_id 是排序第一优先级 - MySQL 8.0+ 应直接用
SUM(amount) OVER (PARTITION BY user_id ORDER BY created_at),别硬套变量
变量方案在 LIMIT 或分页时为什么结果错乱
因为 LIMIT 是在变量计算**之后**才截断结果的。例如你写 SELECT ..., @sum := @sum + x LIMIT 10,变量其实已经对全表所有行执行了累加,只是最后只显示前 10 行——running total 值完全不对。