【好文共分享】关于ora-04065和ora-04068的原理解释
轉自:http://tech.it168.com/oldarticle/2006-05-26/200605252326345.shtml
?
今天在運行一個過程是報了一個ORA-4068錯誤。雖然問題很簡單,而且也很容易解決,但是要真正理解的錯誤產生的原因,還需要對概念理解的比較清晰。
?
下面做一個簡單的例子重現錯誤:
SQL> CREATE TABLE T AS SELECT * FROM TAB;
?表已創建。
SQL> CREATE OR REPLACE PROCEDURE P_RECREATE AS
2 BEGIN
3 EXECUTE IMMEDIATE 'DROP TABLE T';
4 EXECUTE IMMEDIATE 'CREATE TABLE T AS SELECT * FROM TAB';
5 END;
6 /
?過程已創建。
SQL> CREATE OR REPLACE PROCEDURE P_INSERT_T AS
2 BEGIN
3 INSERT INTO T SELECT * FROM T;
4 END;
5 /
?過程已創建。
SQL> BEGIN
2 P_RECREATE;
3 P_INSERT_T;
4 END;
5 /
?BEGIN
?*第 1 行出現錯誤:
?ORA-04068: 已丟棄程序包 的當前狀態
?ORA-04065: 未執行, 已更改或刪除 stored procedure "YANGTK.P_INSERT_T"
?ORA-06508: PL/SQL: 無法找到正在調用 : "YANGTK.P_INSERT_T" 的程序單元
?ORA-06512: 在 line 3
如果單獨執行兩個過程,則不會報錯:
SQL> EXEC P_RECREATE PL/SQL 過程已成功完成。 SQL> EXEC P_INSERT_T PL/SQL 過程已成功完成。
看到ORA-04068錯誤,我首先想到的是由于在P_RECREATE過程中,對表進行了刪除重建工作,導致和這個表相關的存儲過程變為INVALID。
于是,我嘗試在調用過程之前重編譯P_INSERT_T過程:
SQL> BEGIN
2 P_RECREATE;
3 EXECUTE IMMEDIATE 'ALTER PROCEDURE P_INSERT_T COMPILE';
4 P_INSERT_T;
5 END;
6 /
?BEGIN
?*第 1 行出現錯誤:
?ORA-04068: 已丟棄程序包 的當前狀態
?ORA-04065: 未執行, 已更改或刪除 stored procedure "YANGTK.P_INSERT_T"
?ORA-06508: PL/SQL: 無法找到正在調用 : "YANGTK.P_INSERT_T" 的程序單元
?ORA-06512: 在 line 4
但是發現,錯誤依舊。
SQL> BEGIN
2 P_RECREATE;
3 EXECUTE IMMEDIATE 'BEGIN P_INSERT_T; END;';
4 END;
5 /
?PL/SQL 過程已成功完成。
但如果使用動態SQL的方式調用P_INSERT_T過程,則不會報錯。
問題已經基本上清楚了,但是要想說明白,還需要從頭說起。
存儲過程在編譯時,自動檢查語法錯誤、權限以及所有對象依賴性等。等到執行的時候,Oracle不會再進行類似的檢查,而是直接運行過程,這也是存儲過程擁有較高效率的一個原因。
當存儲過程依賴的對象發生變化了,Oralce會自動將存儲過程的狀態置為INVALID,而存儲過程的狀態如果為INVALID,則會在下次執行的時候嘗試重新編譯,如果編譯通過,則繼續執行,編譯失敗則報錯。
這就是為什么兩個過程單獨執行時不會報錯。
那么,為什么兩個過程放到一起執行就會報錯,即使嘗試重新編譯也無效呢。這是由于導致過程P_INSERT_T失效的過程就在調用P_INSERT_T過程的匿名塊中。在將匿名塊提交給Oracle時,Oracle對里面每個過程的狀態進行了檢查,由于導致P_INSERT_T失效的P_RECREATE過程還沒有執行,這時候,所有過程的狀態都是VALID,于是Oracle記錄下來過程的信息準備到直接運行。但是調用P_RECREATE過程后,由于T表被刪除重建,P_INSERT_T的狀態發生變化,但是Oracle對過程P_INSERT_T的檢查已經完成,因此在嘗試直接運行P_INSERT_T的代碼的時候發現P_INSERT_T的狀態已經發生變化,因此,這里報錯ORA-04068,同樣的道理,即使對P_INSERT_T進行了重新編譯,Oracle在執行時發現檢查時的代碼已經發生了變化,仍然會報錯,即使這個時候存儲過程的狀態已經時VALID了。
而采用動態SQL不會報錯的原因就更容易理解了,由于采用動態SQL,Oracle將編譯是進行的操作推遲到運行時進行,也就是說,Oracle會在調用P_RECREATE 之后,調用P_INSERT_T過程之前對P_INSERT_T進行檢查并重新編譯,因此,采用動態SQL不會報錯。
總結
以上是生活随笔為你收集整理的【好文共分享】关于ora-04065和ora-04068的原理解释的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 实例讲解sed的9种常见用法
- 下一篇: you may be a victim