oracle批量更新出错默认中断,需用log errors子句跳过失败行;必须先用dbms_errlog.create_error_log创建日志表,再在update末尾指定log errors into、reject limit unlimited等参数,错误信息将写入日志表且不随主事务回滚。

Oracle批量更新出错就中断?用LOG ERRORS子句让失败行跳过
默认情况下,UPDATE语句只要某一行触发约束、类型转换失败或触发器抛异常,整个语句立刻回滚并报错。想让其他合法行继续执行,必须显式启用错误日志机制——不是靠EXCEPTION块,也不是靠分批提交,而是靠LOG ERRORS子句配合预建的错误日志表。
必须先建错误日志表,否则LOG ERRORS直接报ORA-38901
LOG ERRORS不会自动建表,你得先用DBMS_ERRLOG.CREATE_ERROR_LOG过程生成一张结构固定的日志表。它会记录出错行的ROWID、错误码、错误消息和原始字段值(最多支持255列)。
常见错误现象:ORA-38901: invalid error logging table name,说明没建表或表名写错;ORA-38902: invalid column name in error logging table则多因目标表含长对象(如CLOB)且未在建日志表时指定SKIP_UNUSABLE_INDEXES参数。
- 执行前确认用户有
EXECUTE权限在DBMS_ERRLOG - 建表命令示例:
DBMS_ERRLOG.CREATE_ERROR_LOG(dml_table_name => 'EMP', err_log_table_name => 'ERR$_EMP'); - 生成的日志表名默认加
ERR$前缀,字段含ORA_ERR_NUMBER$(错误码)、ORA_ERR_MESG$(错误文本)、ORA_ERR_ROWID$(原行ROWID)等
LOG ERRORS子句的位置和参数影响错误捕获范围
它必须紧接在UPDATE语句末尾,不能放在WHERE之后或RETURNING之前。不带参数时只记录前30个字符的错误消息;加REJECT LIMIT UNLIMITED才能确保所有错误行都进日志表(默认只允许10条错误,超限仍报错)。
使用场景:ETL清洗数据时,主键冲突或数值超长很常见,但你想先跑通大部分更新,再单独查ERR$_EMP分析失败原因。
- 正确写法:
UPDATE emp SET salary = salary * 1.1 WHERE dept_id = 10 LOG ERRORS INTO ERR$_EMP ('UPDATE_EMP') REJECT LIMIT UNLIMITED; -
'UPDATE_EMP'是可选标记,会存入ORA_ERR_TAG$字段,方便区分不同批次的错误 - 若省略
INTO子句,Oracle用默认日志表名(如ERR$_EMP),但前提是该表已存在且结构匹配
错误日志表里的数据怎么用?别直接删,先核对再处理
日志表里每条记录对应一条失败的原始行,但注意:ORA_ERR_ROWID$字段在表被TRUNCATE或MOVE后失效;如果更新涉及分区表且行迁移了,ROWID也可能无效。所以不能无脑用SELECT ... FROM emp WHERE ROWID = :rid去捞原数据。
性能影响很小——日志插入走的是常规DML,但高并发下ERR$_xxx表可能成热点,建议给它加索引(如CREATE INDEX idx_err_emp_tag ON ERR$_EMP(ORA_ERR_TAG$);)。
- 查失败原因:
SELECT ORA_ERR_TAG$, ORA_ERR_NUMBER$, ORA_ERR_MESG$ FROM ERR$_EMP WHERE ORA_ERR_TAG$ = 'UPDATE_EMP'; - 还原原始值需结合
ORA_ERR_OPTYP$(操作类型)和各字段的$后缀列(如SALARY$存更新前值) - 清空日志表前,务必确认已归档或修复问题,否则下次
LOG ERRORS又写进去会覆盖标记
最易忽略的点:错误日志表本身不参与事务控制——即使主UPDATE回滚,已写入日志表的记录也不会回滚。这意味着你要手动管理日志生命周期,不能依赖事务一致性。











