on duplicate key update 中累加字段需用 ifnull(cnt, 0) + 1 避免 null 导致结果为 null,values(cnt) + 1 错误因取插入值而非原值,子查询场景也需提前处理 null。

ON DUPLICATE KEY UPDATE 里不能直接用 SET column = column + value
很多人写 INSERT INTO t (id, cnt) VALUES (1, 1) ON DUPLICATE KEY UPDATE cnt = cnt + 1,看起来没问题,但实际执行时如果 cnt 当前是 NULL,结果会变成 NULL —— 因为 NULL + 1 在 MySQL 中仍是 NULL。这不是语法错误,而是语义陷阱。
常见触发场景:表刚建好、某行首次插入后被部分更新、或字段允许 NULL 且未显式初始化。
- 务必确认目标列是否允许
NULL;若允许,必须用IFNULL()或COALESCE()做兜底 - 推荐统一用
IFNULL(cnt, 0),语义清晰,性能开销可忽略 - 不要依赖默认值(如
DEFAULT 0)来规避——ON DUPLICATE KEY UPDATE不触发默认值逻辑
正确写法:用 IFNULL 包裹累加表达式
核心就是把裸的 cnt = cnt + 1 改成 cnt = IFNULL(cnt, 0) + 1。MySQL 会在冲突发生时读取当前行的 cnt 值(可能是 NULL),再安全计算。
完整示例:
INSERT INTO stats (user_id, login_count) VALUES (123, 1) ON DUPLICATE KEY UPDATE login_count = IFNULL(login_count, 0) + 1;
- 如果
user_id是主键或唯一索引,且该行已存在、login_count为NULL,这次更新后值为1 - 如果原值是
5,则更新为6;如果是0,则为1 - 多个字段累加?每个都独立套
IFNULL,比如views = IFNULL(views, 0) + 1, shares = IFNULL(shares, 0) + 1
为什么不用 VALUES() 函数?它不适用于累加
有人尝试 cnt = VALUES(cnt) + 1,这是错的。VALUES(col) 只能取本次 INSERT 语句中对应列的**原始输入值**,不是当前行旧值。所以 VALUES(cnt) + 1 永远是 “输入值 + 1”,而非 “旧值 + 1”。
-
VALUES(cnt)在INSERT INTO t(c) VALUES (10)中返回10,不管原行cnt是多少 - 想做累计,必须引用原字段名(如
cnt),再用IFNULL保护 - 误用
VALUES()会导致数据跳变,比如连续两次执行VALUES(cnt)+1,每次都从 10 开始加,变成 11→12,而不是 10→11→12
注意 INSERT ... SELECT 场景下的 NULL 传播风险
当 INSERT 来源是子查询(比如 INSERT INTO t SELECT ... FROM other_t),子查询结果中的 NULL 会直接进入 VALUES 阶段,进而影响 ON DUPLICATE KEY UPDATE 的计算逻辑。
- 即使目标表列定义了
NOT NULL DEFAULT 0,子查询返回的NULL仍会覆盖该默认值 - 稳妥做法是在子查询里就处理:
SELECT user_id, IFNULL(sum_logins, 0) AS sum_logins FROM ... - 或者在
UPDATE子句里双重防护:cnt = IFNULL(IFNULL(cnt, 0), 0) + 1(虽然冗余,但杜绝意外)
NULL,累加就断了。别指望 MySQL 自动帮你填 0。











