MERGE在高并发下触发ORA-00001,因其非原子性:先判断ON条件再执行INSERT/UPDATE,两个事务可能同时判定“不存在”而并发INSERT,撞上唯一约束;这是典型的“检查-然后-执行”竞态。

为什么MERGE在并发下会触发ORA-00001而不是静默更新?
MERGE本身不是原子操作,Oracle执行时分两步:先查ON条件是否匹配,再决定走UPDATE还是INSERT。两个并发事务可能同时判断“目标行不存在”,都进入INSERT分支,最终撞上唯一约束报ORA-00001。这不是语法错,是典型的“检查-然后-执行”竞态——数据库没义务帮你串行化这个判断过程。
ORA-30926错误和主键冲突有关系吗?
有,但不是同一类问题:ORA-30926表示源数据不干净,比如USING子句里order_id重复了,导致一个目标行被多条源行匹配;而ORA-00001是目标表已有该主键,INSERT分支撞上了约束。两者常一起出现,但根因不同:
-
ORA-30926发生在MERGE校验阶段,甚至没走到DML执行 -
ORA-00001发生在INSERT实际执行时,说明ON逻辑看似正确,但并发破坏了前提 - 如果源表本身有重复+高并发,可能先报
ORA-30926,修复后又暴露ORA-00001
WHERE子句加在WHEN MATCHED后真能防重启动吗?
能,而且必须加。Oracle在某些版本存在DML重启动缺陷:当UPDATE执行中途发现行被其他事务改过,会回退并重试,但重试时可能用旧的ON结果去更新——比如你本意是“id=1时更新name”,重启动后却把id=2的行也更新了。显式写WHERE t1.id = 1能锁死作用范围,且必须和ON里的谓词完全一致,否则无效。
比MERGE更稳的替代方案是什么?
用PL/SQL块做INSERT + 异常捕获:
BEGIN
INSERT INTO test_dml_restart (id, name) VALUES (1, 'Tom');
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
UPDATE test_dml_restart SET name = 'Tom' WHERE id = 1;
END;
这种写法天然串行化,因为PL/SQL块内语句按顺序执行,不会出现两个会话同时走到UPDATE分支。它还更容易调试:你可以单独跑INSERT看是否真冲突,而不是怀疑ON逻辑写错了。真正麻烦的从来不是语法怎么写,而是你有没有验证过源数据在关联键上确实无重复——这往往要追到上游ETL清洗环节。

















