merge比update from更可控,因其是原子性dml操作,一次扫描完成匹配、更新、插入,避免join放大和隐式重复更新,并在一对多时主动报错而非静默选行。

为什么MERGE比UPDATE FROM更可控
MERGE在SQL Server中不是语法糖,而是原子性DML操作:它把匹配、更新、插入三阶段合并为一次扫描,避免了UPDATE FROM中因JOIN放大导致的隐式重复更新。尤其当源表对目标主键存在一对多关系时,UPDATE ... FROM会静默选任意一行(无确定性),而MERGE直接报错或按预期跳过——前提是ON条件写得准。
ON子句只能写target和source之间的等值匹配
ON不是WHERE,不能塞业务过滤逻辑。常见翻车点是把WHERE customer.status = 'active'硬塞进ON里,结果要么语法报错,要么匹配逻辑崩坏。
- 错误写法:
ON t.sku_id = s.sku_id AND s.status = 'active' - 正确做法:把
status = 'active'移到USING子查询里过滤,ON只留t.sku_id = s.sku_id - 若需按不同条件分路径更新(比如VIP用户加权、普通用户不更新),用
WHEN MATCHED AND s.tier = 'VIP'分支控制,别动ON
USING必须提供明确、去重的源数据集
子查询是MERGE的“安全阀”。你不能让USING返回重复sku_id,否则MERGE可能拒绝执行(SQL Server 2016+默认启用ERROR_FOR_DIVISION_BY_ZERO类严格模式)。
- 聚合必须在子查询内完成,例如
SUM(o.qty) AS delta,不能指望MERGE帮你GROUP BY - 避免
LEFT JOIN引入NULL导致ON匹配失败;宁可用INNER JOIN+ 补缺逻辑(如COALESCE(s.delta, 0)) - 如果源数据来自多张表,先用CTE或嵌套子查询拼出宽表,再喂给USING,不要在USING里直接写三表JOIN
WHEN NOT MATCHED时INSERT字段必须与SELECT列一一对应
这是新手最常漏掉的细节:INSERT子句里的字段列表和VALUES里的表达式顺序、类型、数量必须严格对齐,且不能引用target表不存在的列。
- 错误示范:
INSERT (id, name) VALUES (s.id, s.name, s.created_at)—— 多了一个值 - 正确写法:
INSERT (id, name, created_at) VALUES (s.id, s.name, s.created_at) - 如果只想插入部分字段,显式写出字段名,并确保VALUES里对应位置有值(可为
DEFAULT或NULL) - 注意:SQL Server中
INSERT ... SELECT允许省略字段列表,但MERGE的INSERT不行——必须写全
SELECT跑一遍USING子查询,确认行数、主键唯一性、NULL分布。MERGE的容错性远低于普通UPDATE,一旦ON条件写歪,可能整张表被误更新——它不会因为你忘了加WHERE就手下留情。










