mysql的insert...on duplicate key update必须依赖主键或唯一索引触发,冲突时执行update;若要“存在则忽略、不存在则插入”,应使用insert ignore(需确保对应字段有唯一索引),否则无效。

MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE 不能直接“存在则更新、不存在则忽略”,它必须配合唯一索引(或主键)触发,且一旦冲突就执行 UPDATE;而“忽略”需要的是不报错也不写入——这得换策略。
用 INSERT IGNORE 实现“存在则忽略,不存在则插入”
这是最接近你字面需求的做法:当违反 UNIQUE 或 PRIMARY KEY 约束时,语句静默跳过,不报错也不插入。
- 必须确保目标字段(如
id或email)上有UNIQUE索引,否则IGNORE不生效 - 它完全不支持“存在则更新”,冲突时连
UPDATE的机会都没有 - 注意:MySQL 8.0+ 中,
INSERT IGNORE会抑制所有警告(包括非唯一性错误),可能掩盖其他问题
INSERT IGNORE INTO users (id, name, email) VALUES (1, 'Alice', 'a@example.com');
用 INSERT ... ON DUPLICATE KEY UPDATE 实现“存在则更新,不存在则插入”
这才是 MySQL 原生支持的 upsert 行为,但它的逻辑是“插入优先,冲突才更新”,不是“先查再判”。
- 同样依赖
UNIQUE或PRIMARY KEY字段来判定“存在” - 如果只想更新部分字段,直接写
name = VALUES(name),VALUES(col)表示本次INSERT中该列的值 - 如果想“存在则更新,不存在则什么也不做”,可以故意让
UPDATE赋值为自身(如id = id),但这仍会触发一次无意义的更新,影响affected_rows计数和 binlog
INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'a@example.com') ON DUPLICATE KEY UPDATE name = VALUES(name), email = VALUES(email);
PostgreSQL 怎么办?用 INSERT ... ON CONFLICT DO NOTHING/UPDATE
PostgreSQL 的语法更精确,也更贴近你的描述:“冲突时可选忽略或更新”。
-
ON CONFLICT (column)显式指定判断依据,比 MySQL 更可控 -
DO NOTHING对应“存在则忽略”,DO UPDATE SET ...对应“存在则更新” - 注意:
ON CONFLICT只响应UNIQUE违反,不响应外键或检查约束
INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'a@example.com') ON CONFLICT (id) DO NOTHING;
INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'a@example.com') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, email = EXCLUDED.email;
为什么不能用 SELECT + INSERT/UPDATE 手动判断?
看似直观,但在并发场景下大概率出错:两次请求同时查到“不存在”,然后都执行 INSERT,触发唯一键冲突或导致重复数据。
- 除非加
SELECT ... FOR UPDATE锁住范围,但这引入事务复杂度和性能瓶颈 - 应用层重试(捕获唯一键错误后改发 UPDATE)可行,但代码分散、不易维护
- 真正安全又简洁的方案,始终是数据库原生的 upsert 机制,而不是应用层模拟
关键点容易被忽略:所有 upsert 行为都强依赖唯一索引定义——没有它,数据库根本不知道“存在”指什么。别只写 SQL,先确认 CREATE UNIQUE INDEX 已执行。










