ORA-04091变异表错误排查:触发器未更新触发表却报错
ORA-04091错误原因及修复方案
错误原因
- 你的触发器是行级触发器(FOR EACH ROW),触发时绑定的
SPEC_DDS表正处于变异状态——此时DML操作(INSERT/UPDATE/DELETE)还未完全执行,表中数据处于中间不一致的状态。Oracle为了保障数据一致性,禁止行级触发器直接访问触发它的表,哪怕只是查询而非更新。 - 报错中的
WKSP_ERPM.SPEC_DM_DATA_SLICES应该是SPEC_DDS的全限定表名或别名,本质就是触发表本身。
修复方案
方案一:改用语句级触发器(推荐,适配当前逻辑)
当前触发器的逻辑是对SPEC_DP做批量全局更新,不需要处理单条行的新旧值,完全可以去掉FOR EACH ROW改为语句级触发器。语句级触发器在整个DML操作完成后执行,此时SPEC_DDS表已稳定,不会触发变异表错误。
修改后的触发器代码:
create or replace TRIGGER trg_** AFTER INSERT OR UPDATE OR DELETE ON SPEC_DDS -- 移除FOR EACH ROW,默认就是语句级触发器 BEGIN -- 重置所有SPEC_DP的sdd_enabled为no UPDATE SPEC_DP SET sdd_enabled = 'no'; -- 将符合条件的SPEC_DP行设为yes UPDATE SPEC_DP SET sdd_enabled = 'yes' WHERE spec_id IN ( SELECT spec_id FROM ERPM_SPECS WHERE artifact_id IN ( SELECT TO_NUMBER(data_slice_id) FROM SPEC_DDS WHERE REGEXP_LIKE(data_slice_id, '^[0-9]+$') ) ); END; /
方案二:使用复合触发器(若需处理行级数据)
如果后续需要用到触发行的:NEW/:OLD值,可以用复合触发器,分阶段收集数据再执行更新:
create or replace TRIGGER trg_** FOR INSERT OR UPDATE OR DELETE ON SPEC_DDS COMPOUND TRIGGER -- 定义集合存储有效数据切片ID TYPE t_data_slice_ids IS TABLE OF NUMBER; v_collected_ids t_data_slice_ids := t_data_slice_ids(); AFTER EACH ROW IS BEGIN -- 收集当前操作行的有效data_slice_id IF INSERTING OR UPDATING THEN IF REGEXP_LIKE(:NEW.data_slice_id, '^[0-9]+$') THEN v_collected_ids.EXTEND; v_collected_ids(v_collected_ids.LAST) := TO_NUMBER(:NEW.data_slice_id); END IF; END IF; IF DELETING THEN IF REGEXP_LIKE(:OLD.data_slice_id, '^[0-9]+$') THEN v_collected_ids.EXTEND; v_collected_ids(v_collected_ids.LAST) := TO_NUMBER(:OLD.data_slice_id); END IF; END IF; AFTER STATEMENT IS BEGIN -- 重置所有SPEC_DP的sdd_enabled为no UPDATE SPEC_DP SET sdd_enabled = 'no'; -- 如果有收集到有效ID,更新对应行 IF v_collected_ids.COUNT > 0 THEN UPDATE SPEC_DP SET sdd_enabled = 'yes' WHERE spec_id IN ( SELECT spec_id FROM ERPM_SPECS WHERE artifact_id MEMBER OF v_collected_ids ); END IF; END AFTER STATEMENT; END trg_**; /
内容的提问来源于stack exchange,提问作者Divya Rose
相关产品推荐
相关产品推荐

