MERGE是SQL Server中用于原子化同步目标表的专用工具,基于ON等值联接条件,按WHEN MATCHED(更新/删除)、NOT MATCHED BY TARGET(插入)、NOT MATCHED BY SOURCE(删除)三类行关系执行对应操作,须配合OUTPUT $ACTION获取操作类型,且禁止源表重复键或目标表自引用。

MERGE 在 SQL Server 中不是“更高级的 INSERT/UPDATE”,而是解决「基于源数据批量同步目标表」这一具体问题的专用工具。它本身不替代单条 DML,但能在一个原子事务里完成三类操作,避免竞态和中间状态。
WHEN MATCHED、NOT MATCHED BY TARGET、NOT MATCHED BY SOURCE 各自触发什么
这三个子句对应三类行关系,必须按逻辑顺序理解:
-
WHEN MATCHED:源表和目标表在ON条件下有匹配行(比如id相同)→ 执行UPDATE或DELETE -
WHEN NOT MATCHED BY TARGET:源表有、目标表没有(ON条件找不到)→ 执行INSERT -
WHEN NOT MATCHED BY SOURCE:目标表有、源表没有 → 执行DELETE(注意:不是UPDATE)
常见错误是把 NOT MATCHED 当成“任意不匹配”,漏写 BY TARGET 或 BY SOURCE,导致语法报错或逻辑错位。SQL Server 不接受裸 WHEN NOT MATCHED(除非你明确省略 BY TARGET,此时默认等价于 BY TARGET)。
MERGE 的 ON 条件必须是确定性联接,不能用模糊匹配
ON 子句决定了哪些行被归类到哪一类分支,它必须是可精确判断的等值条件(如 t.id = s.id),不能含函数、LIKE、IS NULL 等非 SARGable 表达式。否则会报错:The MERGE statement attempted to UPDATE or DELETE the same row more than once.
- 错误示例:
ON t.name = s.name COLLATE SQL_Latin1_General_CP1_CI_AS(隐式转换风险) - 正确做法:确保联接字段类型一致、有索引、无空值干扰;必要时先清洗源数据
- 如果业务上真需模糊匹配(如拼音首字母),得提前在源表中生成确定性键,再用于
ON
OUTPUT 子句是唯一能拿到每行操作类型的手段
执行 MERGE 时,你无法直接知道哪几行被插入、哪几行被更新。靠 OUTPUT $ACTION 是最可靠方式:
OUTPUT $ACTION, INSERTED.id, DELETED.id
返回结果中 $ACTION 字段值为 'INSERT'、'UPDATE' 或 'DELETE'。这个输出可用于日志记录、触发下游处理,或调试逻辑是否符合预期。漏掉 OUTPUT 就等于“干了但不知道干了啥”,尤其在线上同步任务中容易掩盖数据漂移。
注意:OUTPUT 必须紧跟在所有 WHEN 分支之后、分号之前;且不能引用未在 INSERTED 或 DELETED 中出现的列。
MERGE 不支持对同一目标行多次操作,也不支持子查询中修改目标表
这是最容易被忽略的硬限制:
- 源表中若存在重复
ON键(如两个id=5的行),SQL Server 会直接报错:The MERGE statement attempted to UPDATE or DELETE the same row more than once. - 目标表不能出现在
USING子句的子查询里(即不能USING (SELECT ... FROM target_table)),否则报错:Invalid use of side-effecting or time-dependent operator in 'MERGE' statement. - 没有
TOP限制时,整个语句要么全成功,要么全回滚;加TOP (n)可控范围,但要注意它截断的是匹配后的结果集,不是源表原始行数
实际写法中,务必先 GROUP BY 或 DISTINCT 源数据去重,并验证 ON 字段在目标表上的唯一性约束是否存在。

















