1. 程式人生 > >ORA-04091錯誤原因與解決方法

ORA-04091錯誤原因與解決方法

最近工作中寫了一觸發器報錯:ORA-04091:table XX  is mutating, trigger/function may not see it。

下面通過官方文件及網友提供資料分析一下錯誤原因及解決方法:

1.檢視oracle官方文件:

原因:觸發器(或者被語句中引用的使用者自定義PL/SQL函式)檢視去查詢(或修改)一個被另一語句修改而觸發的表。

解決方法:重寫觸發器(或函式)避免讀該表。

2.根據錯誤原因我們寫如下觸發器,重現錯誤:

 使用scott方案,建立一下表、觸發器:

  1. SQL> createtable tr_table asselect * 
    from emp;  
  2. 表已建立。  
  3. SQL> edit  
  4. 已寫入 file afiedt.buf  
  5.   1  createorreplacetrigger tr_test  
  6.   2  afterupdateon emp  
  7.   3  for each row  
  8.   4  begin
  9.   5     update tr_table t set t.sal = (select sal from emp where empno=t.empno and empno=:new.empno);  
  10.   6* end;  
  11. SQL> /  
  12. 觸發器已建立  
  13. SQL> update
     emp set sal=3700 where empno=7788;  
  14. update emp set sal=3700 where empno=7788  
  15.        *  
  16. 第 1 行出現錯誤:  
  17. <span style="color:#ff0000;">ORA-04091: 表 SCOTT.EMP 發生了變化, 觸發器/函式不能讀它  
  18. </span>ORA-06512: 在 "SCOTT.TR_TEST", line 2  
  19. ORA-04088: 觸發器 'SCOTT.TR_TEST' 執行過程中出錯  

3.原因分析:

在Oracle中執行DML語句的時候是需要顯示進行提交操作的。當我們進行插入的時候,會觸發觸發器執行對觸發器作用表和擴充套件表的種種操作,但是這個時候觸發器和插入語句是在同一個事務管理中的,因此在插入語句沒有被提交的情況下,我們無法對觸發器作用表進行其他額外的操作。如果執行其他額外的操作則會丟擲如上異常資訊。

4.解決方案:

1) 我們知道,出錯的原因是因為觸發器和DML語句在同一事務管理中,所以方案一便是將觸發器和DML語句分成兩個單獨的事務處理。這裡可以使用Pragma autonomous_transaction; 告訴Oracle觸發器是自定義事務處理。

SQL語句如下:

  1. createorreplacetrigger tr_test  
  2. afterupdateon 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. createorreplacetrigger tr_test  
  2.  afterupdateon emp  
  3.  for each row  
  4.  begin
  5.      update tr_table t set t.sal = :new.sal;  
  6.  end;  
  7. /  

5. 再次插入資料:

  1. SQL> update emp set sal=3800 where empno=7788;  
  2. 已更新 1 行。  
  3. SQL> commit;  
  4. 提交完成。  
  1. SQL> select * from tr_table;  
  2.      EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO  
  3. ---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
  4.       7369 SMITH      CLERK           7902 17-12月-80            800                    20  
  5.       7499 ALLEN      SALESMAN        7698 20-2月 -81           1800        300         30  
  6.       7521 WARD       SALESMAN        7698 22-2月 -81           1250        500         30  
  7.       7566 JONES      MANAGER         7839 02-4月 -81           2975                    20  
  8.       7654 MARTIN     SALESMAN        7698 28-9月 -81           1250       1400         30  
  9.       7698 BLAKE      MANAGER         7839 01-5月 -81           2850                    30  
  10.       7782 CLARK      MANAGER         7839 09-6月 -81           2450                    10  
  11.       <span style="BACKGROUND-COLOR: #009900">7788 SCOTT      ANALYST         7566 19-4月 -87           3800                    20  
  12. </span>      7839 KING       PRESIDENT            17-11月-81           5000                    10  
  13.       7844 TURNER     SALESMAN        7698 08-9月 -81           1500          0         30  
  14.       7876 ADAMS      CLERK           7788 23-5月 -87           1100                    20  
  15.      EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO  
  16. ---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
  17.       7900 JAMES      CLERK           7698 03-12月-81            950                    30  
  18.       7902 FORD       ANALYST         7566 03-12月-81           3000                    20  
  19.       7934 MILLER     CLERK           7782 23-1月 -82           1300                    10  
  20. 已選擇14行。