• ORA-04091错误原因与解决方法


    最近工作中写了一触发器报错:ORA-04091:table XX  is mutating, trigger/function may not see it。

    下面通过官方文档及网友提供资料分析一下错误原因及解决方法:

    1.查看oracle官方文档:

    原因:触发器(或者被语句中引用的用户自定义PL/SQL函数)视图去查询(或修改)一个被另一语句修改而触发的表。

    解决方法:重写触发器(或函数)避免读该表。

     

    2.根据错误原因我们写如下触发器,重现错误:

     使用scott方案,创建一下表、触发器:

    1. SQL> create table tr_table as select * from emp;  
    2.   
    3. 表已创建。  
    4.   
    5. SQL> edit  
    6. 已写入 file afiedt.buf  
    7.   
    8.   1  create or replace trigger tr_test  
    9.   2  after update on emp  
    10.   3  for each row  
    11.   4  begin  
    12.   5     update tr_table t set t.sal = (select sal from emp where empno=t.empno and empno=:new.empno);  
    13.   6* end;  
    14. SQL> /  
    15.   
    16. 触发器已创建  
    17.   
    18. SQL> update emp set sal=3700 where empno=7788;  
    19. update emp set sal=3700 where empno=7788  
    20.        *  
    21. 第 1 行出现错误:  
    22. <span style="color:#ff0000;">ORA-04091: 表 SCOTT.EMP 发生了变化, 触发器/函数不能读它  
    23. </span>ORA-06512: 在 "SCOTT.TR_TEST", line 2  
    24. ORA-04088: 触发器 'SCOTT.TR_TEST' 执行过程中出错  


     

    3.原因分析:

    在Oracle中执行DML语句的时候是需要显示进行提交操作的。当我们进行插入的时候,会触发触发器执行对触发器作用表和扩展表的种种操作,但是这个时候触发器和插入语句是在同一个事务管理中的,因此在插入语句没有被提交的情况下,我们无法对触发器作用表进行其他额外的操作。如果执行其他额外的操作则会抛出如上异常信息。

     

    4.解决方案:

    1) 我们知道,出错的原因是因为触发器和DML语句在同一事务管理中,所以方案一便是将触发器和DML语句分成两个单独的事务处理。这里可以使用Pragma autonomous_transaction; 告诉Oracle触发器是自定义事务处理。

    SQL语句如下:

     

    1. create or replace trigger tr_test  
    2. after update on emp  
    3. for each row  
    4.   declare  
    5.   pragma autonomous_transaction;  
    6. begin  
    7.     update tr_table t set t.sal = (select sal from emp where empno=t.empno and empno=:new.empno);  
    8.     commit--此处需要显示提交  
    9. end;  

     

    注:以上语句并不能实时获得更新的值。。。原因是我们在update emp表后还没来得及提交sal就触发了触发器,这个时候获取到的只能是老的sal值。

    2) 在Oracle Trigger中有:new,:old两个特殊变量,当触发器为行级触发器的时候,触发器就会提供new和old两个保存临时行数据的特殊变量,我们可以从俩个特殊的变量中取出数据执行扩张表的DML操作。

    SQL语句如下:

     

    1.  create or replace trigger tr_test  
    2.  after update on emp  
    3.  for each row  
    4.  begin  
    5.      update tr_table t set t.sal = :new.sal;  
    6.  end;  
    7. /  
    8.   
    9.    

    5. 再次插入数据:

     

    1. SQL> update emp set sal=3800 where empno=7788;  
    2.   
    3. 已更新 1 行。  
    4.   
    5. SQL> commit;  
    6.   
    7. 提交完成。  


    1. SQL> select * from tr_table;  
    2.   
    3.      EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO  
    4. ---------- ---------- --------- ---------- -------------- ---------- ---------- ----------  
    5.       7369 SMITH      CLERK           7902 17-12月-80            800                    20  
    6.       7499 ALLEN      SALESMAN        7698 20-2月 -81           1800        300         30  
    7.       7521 WARD       SALESMAN        7698 22-2月 -81           1250        500         30  
    8.       7566 JONES      MANAGER         7839 02-4月 -81           2975                    20  
    9.       7654 MARTIN     SALESMAN        7698 28-9月 -81           1250       1400         30  
    10.       7698 BLAKE      MANAGER         7839 01-5月 -81           2850                    30  
    11.       7782 CLARK      MANAGER         7839 09-6月 -81           2450                    10  
    12.       <span style="BACKGROUND-COLOR: #009900">7788 SCOTT      ANALYST         7566 19-4月 -87           3800                    20  
    13. </span>      7839 KING       PRESIDENT            17-11月-81           5000                    10  
    14.       7844 TURNER     SALESMAN        7698 08-9月 -81           1500          0         30  
    15.       7876 ADAMS      CLERK           7788 23-5月 -87           1100                    20  
    16.   
    17.      EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO  
    18. ---------- ---------- --------- ---------- -------------- ---------- ---------- ----------  
    19.       7900 JAMES      CLERK           7698 03-12月-81            950                    30  
    20.       7902 FORD       ANALYST         7566 03-12月-81           3000                    20  
    21.       7934 MILLER     CLERK           7782 23-1月 -82           1300                    10  
    22.   
    23. 已选择14行。  
  • 相关阅读:
    [转] 《大腕》——编程高手篇
    [转] 如何用VB.Net创建一个三层的数据库应用程序
    [转] 张孝祥的java试题
    [转] 很久以前的一个sql面试题及答案.
    [转] C#编程实践
    [转] html技巧
    [转] 揭开SVCHOST.exe进程之谜
    [转] c#.net常用函数和方法集
    [转] Visual Studio.Net 快捷键表
    [转] left join/right join/inner join操作演示
  • 原文地址:https://www.cnblogs.com/wanghang/p/6299201.html
Copyright © 2020-2023  润新知