sql server的merge必须显式写出when matched、when not matched by target、when not matched by source三个分支,缺一即报语法错误;oracle仅支持前两个分支,无not matched by source;mysql无merge,需用insert...on duplicate key update替代。

SQL Server 的 MERGE 语句必须显式指定 WHEN MATCHED 和 WHEN NOT MATCHED
不写全分支会导致语法错误,比如只写 WHEN MATCHED 而漏掉 WHEN NOT MATCHED,SQL Server 会直接报错 Incorrect syntax near the keyword 'MERGE'。这不是可选逻辑,而是语法强制要求。
实际写法中建议始终包含全部三个分支(WHEN MATCHED、WHEN NOT MATCHED BY TARGET、WHEN NOT MATCHED BY SOURCE),哪怕某分支只执行 DO NOTHING。这样既避免语法报错,也显式表达了业务意图:
-
WHEN MATCHED:源和目标主键/唯一键匹配时更新字段(注意不能更新 JOIN 条件列) -
WHEN NOT MATCHED BY TARGET:源里有、目标里没有 → 执行INSERT -
WHEN NOT MATCHED BY SOURCE:目标里有、源里没有 → 可选,用于软删除或归档
Oracle 的 MERGE 不支持 WHEN NOT MATCHED BY SOURCE 分支
Oracle 12c 及之前版本的 MERGE 仅支持 WHEN MATCHED 和 WHEN NOT MATCHED,没有 SQL Server 那种三路分支能力。想实现“目标存在但源不存在时做处理”,只能拆成两步:先 MERGE,再单独 DELETE 或 UPDATE 目标表中不在源数据里的记录。
另外注意 Oracle 对 ON 条件更严格:不能在 ON 子句里用子查询或复杂表达式,否则报错 ORA-00904: invalid identifier。稳妥做法是把过滤逻辑提前到源 CTE 或临时表里:
WITH src AS ( SELECT id, name, status FROM staging_table WHERE is_valid = 1 ) MERGE INTO target t USING src s ON (t.id = s.id) WHEN MATCHED THEN UPDATE SET t.name = s.name WHEN NOT MATCHED THEN INSERT (id, name) VALUES (s.id, s.name);
MERGE 在事务中可能因重复键触发死锁或违反约束
批量合并时如果源数据含重复主键(比如两次导入同一订单号),SQL Server 会在 WHEN NOT MATCHED 阶段尝试多次 INSERT,直接抛出 Violation of PRIMARY KEY constraint。Oracle 则可能卡在唯一索引等待上,导致会话阻塞。
预防方式不是靠重试,而是前置清洗:
- 对源数据先
GROUP BY去重,保留最新时间戳那条 - 用
EXISTS或LEFT JOIN校验目标表是否已存在冲突记录 - 在存储过程中加
TRY...CATCH(SQL Server)或EXCEPTION(Oracle)捕获特定错误号,比如 SQL Server 的2627(唯一约束)或2601(聚集索引重复)
别指望 MERGE 自动跳过冲突行——它不是 UPSERT 的宽容模式,而是原子性操作,一行失败整个语句回滚。
MySQL 没有原生 MERGE,得用 INSERT ... ON DUPLICATE KEY UPDATE 替代
MySQL 用户看到标题别直接套用,它的语法完全不同:MERGE 是 SQL 标准但 MySQL 一直没实现。真正等价的是 INSERT ... ON DUPLICATE KEY UPDATE,且要求目标表必须有 PRIMARY KEY 或 UNIQUE 约束才能触发更新逻辑。
典型陷阱:
- 漏建唯一索引 → 插入重复数据而不更新,静默失败
- 在
ON DUPLICATE KEY UPDATE中引用未在INSERT列表里出现的列,会报错Unknown column in 'field list' - 想实现“存在则更新,不存在则插入,且目标多余行要删除”,MySQL 必须拆成三条语句:
INSERT ... ON DUPLICATE KEY UPDATE+DELETE+WHERE NOT EXISTS子查询
性能上,单次 INSERT ... ON DUPLICATE KEY UPDATE 比模拟 MERGE 的多语句更高效,但批量时要注意 max_allowed_packet 限制,超长 SQL 会被截断。
真正难的不是写出语法正确的 MERGE,而是搞清你用的数据库到底支不支持某个分支、约束条件怎么设、冲突时怎么收口。这些细节不查文档、不看错误号,光靠“应该可以”硬写,十次有八次在凌晨三点被告警叫醒。










