MERGE的ON子句仅用于行分类,不可写业务过滤逻辑;源数据必须去重;WHEN MATCHED可加AND条件分路径更新;DELETE须配NOT MATCHED BY SOURCE且加WHERE过滤。

ON条件只能写等值匹配,别塞WHERE逻辑
MERGE的ON子句不是过滤器,而是行分类的“开关”。它只决定某行属于WHEN MATCHED、WHEN NOT MATCHED BY TARGET还是WHEN NOT MATCHED BY SOURCE——一旦写进业务条件(比如s.status = 'active'),就可能让本该插入的行被漏掉,或让本该报错的重复匹配静默通过。
常见翻车写法:ON t.id = s.id AND s.is_valid = 1
正确做法是把过滤提前到USING里:USING (SELECT id, name FROM source WHERE is_valid = 1) AS s,然后ON t.id = s.id。这样既保证匹配干净,又避免ON膨胀成模糊逻辑。
源数据必须去重,否则MERGE直接报错
SQL Server对重复键极其敏感:只要USING子查询返回两个相同id的行,哪怕目标表只有一条匹配记录,也会触发错误:The MERGE statement attempted to UPDATE or DELETE the same row more than once。
这不是警告,是硬性失败。你不能指望MERGE帮你“选一行”,它只认确定性映射。
- 用
GROUP BY聚合后再喂给USING,比如SUM(qty) AS total_qty - 用
ROW_NUMBER() OVER (PARTITION BY id ORDER BY updated_at DESC)取最新一条 - 绝对不要在
USING里直接LEFT JOIN多张表,先用CTE拼出宽表再引用
WHEN MATCHED后面可以加AND,但别滥用
WHEN MATCHED AND s.tier = 'VIP'是合法且有用的,它让你在一个MERGE里分路径更新:VIP用户加权更新,普通用户跳过;或者只更新last_modified字段而不动业务值。
但注意两点:
- 每个
WHEN MATCHED分支只能出现一次,不能写两个WHEN MATCHED AND ... THEN UPDATE -
AND后的条件是在匹配成功后才判断的,不参与行分类——所以它不会影响NOT MATCHED的判定 - 如果条件复杂(比如涉及函数或NULL判断),建议提前在
USING里计算好布尔列,避免运行时开销
DELETE操作必须配NOT MATCHED BY SOURCE,且慎用
WHEN NOT MATCHED BY SOURCE THEN DELETE看起来方便,但实际很危险:它会删掉所有“源里没有”的目标行,包括那些本该保留的历史记录或软删除标记行。
更安全的做法是显式加WHERE过滤:
WHEN NOT MATCHED BY SOURCE THEN DELETE WHERE t.status != 'archived'
另外注意:NOT MATCHED默认等价于NOT MATCHED BY TARGET,但NOT MATCHED BY SOURCE必须写全,漏掉BY SOURCE会语法报错。
真正容易被忽略的是:DELETE和UPDATE不能同时作用于同一行——MERGE不允许对一个目标行既更新又删除,这会导致编译失败。

















