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

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;










