SQL UPSERT 插入更新冲突解决方法

3次阅读

upsert并非标准sql关键字,而是指“存在则更新、不存在则插入”的操作,各数据库实现不同:postgresql用insert…on conflict,mysql用insert…on duplicate key update,sql server用merge,sqlite用insert…on conflict配合replace/ignore等策略。

SQL UPSERT 插入更新冲突解决方法

SQL 中的 UPSERT(即“插入或更新”)不是标准 SQL 关键字,而是对“当记录存在则更新、不存在则插入”这一行为的统称。不同数据库实现方式差异较大,需根据所用数据库选择对应语法。

PostgreSQL:使用 INSERT … ON CONFLICT

PostgreSQL 从 9.5 版本起支持 INSERT … ON CONFLICT,是最接近原生 UPSERT 的方案。

  • 需指定冲突目标(通常是主键或唯一索引列),例如:ON CONFLICT (id)ON CONFLICT ON CONSTRAINT users_pkey
  • 后续用 DO UPDATE SET 更新已有行,用 EXCLUDED 引用本次想插入但冲突的值
  • 示例:
    INSERT INTO users (id, name, email)  VALUES (1, 'Alice', 'alice@example.com')  ON CONFLICT (id)  DO UPDATE SET name = EXCLUDED.name, email = EXCLUDED.email;

  • 可加 WHERE 条件控制是否更新(如仅当新值非空才覆盖)

MySQL:使用 INSERT … ON DUPLICATE KEY UPDATE

MySQL 用 ON DUPLICATE KEY UPDATE 实现 UPSERT,依赖表中已定义的主键或唯一索引。

  • 冲突触发条件是插入时违反了主键或任意唯一约束
  • VALUES() 函数可引用本次插入的原始值(注意不是 EXCLUDED
  • 示例:
    INSERT INTO users (id, name, email)  VALUES (1, 'Bob', 'bob@example.com')  ON DUPLICATE KEY UPDATE    name = VALUES(name),    email = VALUES(email);

  • 支持在 UPDATE 子句中使用表达式,比如 updated_at = NOW()

SQL Server:使用 MERGE 语句

SQL Server 推荐用 MERGE,功能强大但也更复杂,需明确指定匹配条件和操作分支。

  • 必须包含 WHEN MATCHED(更新)和 WHEN NOT MATCHED(插入)子句
  • 目标表和源数据需显式声明,常配合 VALUES 或子查询作为源
  • 示例:
    MERGE users AS target USING (VALUES (1, 'Charlie', 'charlie@example.com'))        AS source (id, name, email) ON target.id = source.id WHEN MATCHED THEN   UPDATE SET name = source.name, email = source.email WHEN NOT MATCHED THEN   INSERT (id, name, email) VALUES (source.id, source.name, source.email);

  • 注意:MERGE 在某些版本中存在并发异常风险,生产环境建议加适当事务隔离或提示(如 WITH (HOLDLOCK)

SQLite:使用 INSERT … ON CONFLICT REPLACE/IGNORE/ABORT

SQLite 提供 ON CONFLICT 子句,但行为与 PostgreSQL 不同,更偏向冲突策略而非条件更新。

  • REPLACE:删除旧行再插入新行(会触发 delete 和 INSERT 触发器,且自增 ID 可能变化)
  • IGNORE:跳过冲突行,不报错也不更新
  • UPDATE 不是内置选项,如需真正更新,得先用 INSERT OR IGNORE 插入,再用 UPDATE 修改匹配行,或改用 REPLACE 并确保逻辑兼容
  • 示例(模拟更新):
    INSERT OR IGNORE INTO users (id, name, email) VALUES (1, 'David', 'david@example.com'); UPDATE users SET name = 'David', email = 'david@example.com' WHERE id = 1;

text=ZqhQzanResources