MERGE比子查询UPDATE快得多,因Oracle不支持UPDATE...FROM语法,子查询UPDATE易触发嵌套循环与重复扫描,而MERGE是原生“查改合一”操作,优化器可选驱动表、复用连接结果;实测百万级更新从50分钟降至2分钟内。

为什么MERGE比子查询UPDATE快得多
因为Oracle根本不支持UPDATE ... FROM语法,硬写会直接报ORA-00933: sql command not properly ended;而用子查询UPDATE(比如UPDATE t1 SET col = (SELECT col FROM t2 WHERE t2.id = t1.id))在数据量稍大时极易触发嵌套循环、重复扫描目标表,执行计划常崩成全表扫+全表扫。而MERGE INTO是Oracle原生“查改合一”操作,优化器能基于统计信息选驱动表、复用连接结果、避免多次访问t1,实测百万级更新可从50分钟压到2分钟内。
ON子句只放纯关联条件,别塞业务过滤
常见错误是把t2.status = 'ACTIVE'这类条件写进ON,导致本该匹配的行被跳过,甚至拖慢整个匹配过程——因为ON里加非等值条件或函数(如UPPER(t1.code) = UPPER(t2.code))会让索引失效。
- 正确做法:关联字段放
ON,业务逻辑移至UPDATE SET ... WHERE - 确保
t1.id和t2.id都有索引,且类型严格一致(比如都是VARCHAR2(32),别一边CHAR一边VARCHAR2) - 如果
t2里id重复,会报ORA-30926: unable to get a stable set of rows,必须提前去重或用ROW_NUMBER() OVER (PARTITION BY id ORDER BY ...)控制
大批量更新必须分批 + 并行 + 控制触发器
单次跑600万行MERGE INTO可能卡死、锁表太久、归档日志爆满。不能只靠语法切换就完事。
- 加
/*+ PARALLEL(t1, 4) */提示(注意目标表需启用并行DML:ALTER SESSION ENABLE PARALLEL DML) - 用
WHERE ROWNUM 或按主键范围分片,配合<code>COMMIT切段 - 若目标表有触发器,确认是否真需要——它们会在每行上逐个触发,批量时开销巨大;必要时临时禁用:
ALTER TRIGGER xxx DISABLE
只更新不插入?WHEN NOT MATCHED不能省
很多人以为“只做UPDATE”就可以删掉WHEN NOT MATCHED THEN INSERT分支,但这样语法虽通,语义已残缺:后续有人误加INSERT逻辑时,WHEN NOT MATCHED突然冒出来,行为不可控。
- 明确写成
WHEN NOT MATCHED THEN NULL,既合法又表明“此处无插入意图” - 如果源数据来自
SELECT ... FROM dual这种单行构造(比如同步单条员工),USING子句必须能返回预期行数——用SELECT '1001' AS id, 'new' AS name FROM dual,别写成SELECT * FROM target WHERE id = '1001'(查不到就啥都不干)
实际最难的不是写对语法,而是判断哪些字段该进ON、哪些索引真有用、以及分批边界怎么划才不撞锁——这些得看执行计划里的access路径和cardinality估算,不是光背住模板就能绕开的。


















