MySQL变量累计求和必须显式初始化,否则首次计算因@sum为NULL导致全为NULL;必须用ORDER BY保证稳定顺序,且排序字段需唯一;分组累计需双变量模拟PARTITION BY,但易出错,推荐MySQL 8.0+窗口函数。

MySQL变量累计求和必须显式初始化
不初始化就直接用 @sum := @sum + amount,结果一定是错的——因为首次计算时 @sum 是 NULL,整条表达式变成 NULL + amount,后续所有行都为 NULL。必须在查询前或子查询中完成初始化。
常见写法有两种:
-
SELECT @sum := 0单独执行一次(但只对当前会话有效,且容易被忽略) - 更稳妥的是在 FROM 子句里内联初始化:
(SELECT @sum := 0) AS init,确保每次查询都重置
别信“变量自动为 0”的说法,MySQL 对未声明变量的处理是返回 NULL,不是 0。
ORDER BY 不加就乱序,且顺序必须稳定
变量累计依赖执行顺序,而 MySQL 的默认扫描顺序不保证稳定。漏写 ORDER BY 或只按非唯一字段排序(比如只有 created_at),会导致同一语句多次执行结果不同——尤其当时间字段有重复值时,累计值可能跳变、错位。
正确做法:
- 必须带
ORDER BY,且字段要能唯一确定行序 - 若排序依据本身不唯一(如多个订单同秒创建),补上主键或自增 ID 作二级排序:
ORDER BY created_at, id - 避免用
GROUP BY后再套变量,分组打乱原始顺序,变量逻辑彻底失效
变量不能跨 SELECT 语句复用,UPDATE 场景要格外小心
变量作用域仅限单个 SELECT 或 UPDATE 语句。想把累计值写入表字段,不能靠两个独立 SELECT 拼接逻辑;必须用 UPDATE + 变量一次性完成,否则第二次 SELECT 读不到第一次的中间状态。
典型错误模式:
- 先
SELECT @sum := @sum + amount ...查出结果,再另起UPDATE SET col = @sum—— 这里的@sum是上一条语句结束后的最终值,不是每行对应值 - 正确写法是 UPDATE 中直接赋值:
UPDATE sales SET cumulative_sum = (@sum := @sum + amount) ORDER BY id - 注意:UPDATE 的 ORDER BY 在 MySQL 中是合法且必要的,但仅适用于单表,多表 UPDATE 不支持 ORDER BY
分组累计要用变量模拟 PARTITION BY,但极易出错
低版本 MySQL 没有 PARTITION BY,想按用户、品类等分组各自累计,只能靠变量判断边界。核心是用 IF 或 CASE 检查当前行与上一行的分组字段是否变化。
关键细节:
- 必须同时维护两个变量:
@group记上一个分组值,@sum记当前组累计值 - 初始化要设成不可能出现的值(如
@group := ''),否则首行判断失效 - 表达式得写成:
IF(@group = user_id, @sum := @sum + amount, @sum := amount),再更新@group := user_id - 这种写法在并发写入或优化器重排时风险极高,生产环境建议优先升级到 MySQL 8.0+,用原生
SUM() OVER(PARTITION BY user_id ORDER BY created_at)
真正难的不是写出变量语句,而是让结果在任意数据分布、任意执行计划下都稳定可重现——这点变量方案天生脆弱,而窗口函数靠语法强制约束了语义边界。


















