SQL Server 的 MERGE 必须写成 MERGE INTO target,ON 子句仅用于匹配、不可含业务过滤条件,需将过滤移至 USING 子查询;大表需索引避免双全表扫描;INSERT/UPDATE 分支须满足 NOT NULL 和 CHECK 约束;完整同步需三分支(含 WHEN NOT MATCHED BY SOURCE THEN DELETE);执行前应通过执行计划验证索引使用。

MERGE 在 SQL Server 中能同时完成 INSERT、UPDATE、DELETE,但写错就全表扫或报错,不是“标准 SQL 就能跑”
SQL Server 的 MERGE 语法必须带 INTO,且 ON 条件不能写过滤逻辑
常见错误是照搬 Oracle 写法(MERGE target USING source ON ...),SQL Server 要求明确写 MERGE INTO target。更关键的是,ON 子句只负责匹配,不能塞业务过滤条件:
- ✘ 错误写法:
ON t.id = s.id AND t.status != 'deleted'—— 这会让匹配逻辑失效,可能漏删或误更新 - ✓ 正确做法:把
t.status != 'deleted'移到USING的子查询里,比如USING (SELECT * FROM source WHERE active = 1) s - ON 字段必须有索引,否则大表上会触发双全表扫描(源表 + 目标表各扫一遍)
WHEN MATCHED 和 WHEN NOT MATCHED 分支的字段对齐容易踩空值或约束坑
目标表如果有 NOT NULL 字段,而 INSERT 分支没给值,或者给了 NULL,语句直接失败。UPDATE 分支也一样,不能绕过 CHECK 约束:
- INSERT 分支必须显式列出所有
NOT NULL字段,并提供非空值;漏掉created_at或is_active是高频报错点 - UPDATE 分支如果更新
updated_at,建议用GETDATE(),别依赖源表字段——源表该字段可能是 NULL 或旧值 - 两个分支的字段名不要求顺序一致,但数据类型必须兼容,比如目标表是
DECIMAL(10,2),源表传INT没问题,传VARCHAR就可能隐式转换失败
想同步两张表?别忘了 WHEN NOT MATCHED BY SOURCE 才能删多余行
只写 WHEN MATCHED 和 WHEN NOT MATCHED 只能做到“目标表补缺”,做不到“目标表和源表完全一致”。要真正同步,必须加上第三分支:
-
WHEN NOT MATCHED BY SOURCE THEN DELETE—— 这才是让目标表“削足适履”匹配源表的关键 - 但注意:这个分支不支持
OUTPUT子句返回被删行,如需审计,得提前用SELECT把待删 ID 拿出来 - DELETE 操作会触发目标表上的
INSTEAD OF或AFTER触发器,如果目标表有级联逻辑,务必先验证触发器行为
真正难的不是写对语法,而是确认 ON 条件是否真的覆盖了所有需要匹配的业务主键组合,以及目标表索引是否真的生效——EXPLAIN 看执行计划里有没有 Index Seek,比反复试跑更可靠。

















