on duplicate key update 在多唯一索引或联合唯一索引下均有效,仅需一个语句;它在任一唯一键冲突时触发更新,统一处理,不区分冲突来源,且update中用values(col)引用新值。

当表中存在多个唯一约束(如多个 UNIQUE 索引或联合唯一索引)时,ON DUPLICATE KEY UPDATE 仍能生效,但需注意:它只在插入行**触发任意一个唯一键冲突**时才执行更新,且更新逻辑对所有匹配的冲突统一处理,不区分是哪个唯一键导致的冲突。
多唯一索引场景下的写法要点
假设表结构如下:
CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, email VARCHAR(255) UNIQUE, phone VARCHAR(20) UNIQUE, name VARCHAR(100), updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );
此时 email 和 phone 各自为独立唯一索引。只要新插入的记录与现有某行在 email 或 phone 上重复,就会触发 ON DUPLICATE KEY UPDATE。
- SQL 中无需显式指定“因哪个键冲突”,MySQL 自动检测所有唯一约束
-
UPDATE子句中可引用VALUES(col)获取本次 INSERT 尝试传入的值 - 若多列都可能冲突,但只想更新部分字段(如只更新
name和updated_at),直接写即可,无需条件分支
标准多列冲突 INSERT … ON DUPLICATE KEY UPDATE 模板
INSERT INTO users (email, phone, name)
VALUES ('a@example.com', '13800138000', 'Alice')
ON DUPLICATE KEY UPDATE
name = VALUES(name),
updated_at = CURRENT_TIMESTAMP;
说明:
- 即使冲突由
email引起,phone字段也不会被覆盖(除非你主动写phone = VALUES(phone)) -
VALUES(name)表示本次 INSERT 语句中为name提供的值,不是原记录的值 - 若想保留原值、仅在新值非空时更新,可用
IF(VALUES(name) != '', VALUES(name), name)
联合唯一索引(复合唯一键)的写法示例
例如限制同一 user_id 下 category 不可重复:
CREATE TABLE user_settings ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, category VARCHAR(50) NOT NULL, value TEXT, UNIQUE KEY uk_user_cat (user_id, category) );
插入或更新语句写法:
INSERT INTO user_settings (user_id, category, value) VALUES (123, 'theme', 'dark') ON DUPLICATE KEY UPDATE value = VALUES(value), id = LAST_INSERT_ID(id); -- 可选:方便后续获取影响的主键
注意:
- 冲突判断基于
(user_id, category)整体是否已存在,不是单个字段 - 更新子句中不能写
user_id = VALUES(user_id)—— 这虽语法允许,但无意义,因为该组合已存在,user_id值必然不变 - 如需在冲突时记录更新时间,可加
updated_at = CURRENT_TIMESTAMP字段
常见误区与建议
- 不要以为“多个唯一键”就需要写多个
ON DUPLICATE KEY UPDATE—— 语法只支持一个 - 避免在
UPDATE子句中误用列名代替VALUES(col),否则会把原值赋给自己,等于没更新 - 若业务上需区分冲突类型(比如 email 冲突走 A 逻辑、phone 冲突走 B 逻辑),
ON DUPLICATE KEY UPDATE无法满足,应改用SELECT ... FOR UPDATE+ 显式事务处理 - 测试时务必用真实重复数据验证,注意 MySQL 版本对
VALUES()函数的支持一致性(5.7+ 完全支持)










