ON DUPLICATE KEY UPDATE 必须依赖主键或唯一索引才能触发更新,否则退化为普通INSERT;VALUES(col)引用当前插入值,字段名须与表定义一致;并发下易死锁,且不触发INSERT触发器。

直接用,但必须确保表里有主键或唯一索引,否则它根本不会触发更新逻辑,只会报错或静默插入(取决于 SQL mode)。
必须有主键或唯一索引才能生效
ON DUPLICATE KEY UPDATE 不是“智能判断数据是否存在”,它只响应明确的唯一性约束冲突。没有 PRIMARY KEY 或 UNIQUE 索引,这条语句就退化成普通 INSERT,冲突检测压根不启动。
- 常见错误现象:执行后发现重复数据被插入了两遍,或者更新没发生——先查
SHOW CREATE TABLE tbl_test;,确认name字段带UNIQUE,而不是仅加了普通索引 - 唯一索引区分大小写:如果字段用的是
utf8mb4_bin排序规则,'Alice'和'alice'被视为不同值,不会触发更新;换成utf8mb4_0900_as_cs或utf8mb4_general_ci才能按语义去重 - 复合唯一索引也行,比如
UNIQUE KEY idx_uid_type (user_id, event_type),只要VALUES中这两列组合值已存在,就会走更新分支
VALUES() 函数必须写对字段名
VALUES(col_name) 引用的是当前 INSERT ... VALUES(...) 里对应位置的值,不是数据库原记录的值,也不是变量名——括号里填的必须是表的物理字段名,且大小写要和建表时一致。
- 错误写法:
name = VALUES(userName)(实体类字段名)、age = VALUES(@new_age)(用户变量)、update_time = VALUES(NOW())(函数不能放里面) - 正确写法:
age = VALUES(age)、address = VALUES(address)、update_time = NOW()(函数可直接写在等号右边) - 批量插入时,
VALUES(col)仍有效:一次INSERT带多组VALUES,每组都会独立参与冲突判断,VALUES(col)指向各自那组里的值
并发下可能死锁,别在事务里盲目套用
ODKU 在检测冲突时会加间隙锁(gap lock)或记录锁(record lock),如果多个事务同时操作相同唯一键范围,容易形成循环等待。
- 典型场景:两个请求几乎同时执行
INSERT INTO users(email) VALUES('a@b.com') ON DUPLICATE KEY UPDATE status=1,而email是唯一索引——InnoDB 可能给同一间隙加锁,触发Deadlock found when trying to get lock - 缓解方式:控制批量大小(单次不超过 500 行)、避免在长事务中嵌套 ODKU、更新字段尽量少(减少锁持有时间)
- 注意
affected_rows返回值:插入成功返回 1,更新成功返回 2,无变化(如新值和旧值一样)可能返回 0——不能只靠它判断是否“真的改了数据”
最易被忽略的一点:它不会触发 INSERT 触发器,只触发 UPDATE 触发器;如果你依赖 BEFORE INSERT 做字段校验或默认值填充,这部分逻辑在 ODKU 的更新路径里完全不执行。


















