傳遞子程序參數的方式有兩種--傳值和傳引用。當以引用的方式傳遞參數的時候,就將指向實際參數的一個指針傳遞到相應的形式參數。另一方面,當以傳值的方式傳遞參數的時候,就將實際參數的值復制到相應的形式參數。以引用的方式傳遞參數通常會更快,因為它避免了復制。對集合類型的參數而言,這表現更加明顯,因為集合類型的數據一般都非常多。
默認情況下,PL/SQL對IN參數都使用傳引用的方式,而對IN OUT和OUT參數都使用傳值的方式。
1、NOCOPY的使用方法
parameter_name [mode] NOCOPY datatype
其中parameter_name是參數的名稱,mode是參數模式,而datatype是參數類型。如果有NOCOPY,PL/SQL編譯器就會嘗試通過傳引用的方式傳遞參數,而不是通過傳值方式傳遞參數。注意,NOCPY只是一個編譯器提示,而不是編譯器命令,因此,這種提示並不一定總會被接受。
1 create or replace procedure no_copy_proc ( 2 p_inparam in number , 3 p_outparam out nocopy varchar2 , 4 p_inoutparam in out nocopy varchar2 5 )as 6 begin 7 null ; 8 end ;
在IN參數上使用NOCOPY時,會引發一個編譯錯誤,因為IN參數總是以傳引用方式傳遞參數的,因此不允許使用編譯器提示NOCOPY。
2、帶NOCOPY的異常語義
使用NOCOPY即使出現異常,NOCOPY也會自動處理。
以傳遞引用的方式傳遞參數的時候,對形式參數所做的任何更改都會同時反應到實際參數上,因為這二者指向的是同一個位置。這也意味着,如果過程在形式參數的值發生了變化以后,又以一個未處理的異常結束,那么在過程體內修改參數的值將會丟失。
(1)例子:
1 declare 2 v_a number := 10 ; --定義變量 3 v_b number := 20 ; 4 procedure chenge_proc ( 5 p_a in out number , --定義參數 6 p_b in out nocopy number 7 )as 8 begin 9 p_a := 100 ; --修改參數內容 10 p_b := 100 ; --修改參數最直接的影響就是影響原始數據。 11 RAISE_APPLICATION_ERROR(-20001, '測試NOCOPY') ; --拋出異常 12 end ; 13 begin 14 DBMS_OUTPUT.PUT_LINE('過程調用之前:v_a :' || V_A || ' ---- v_b : ' || V_B) ; 15 begin 16 chenge_proc(v_a , v_b) ; --傳遞參數 17 exception 18 when others then 19 DBMS_OUTPUT.PUT_LINE('SQLCODE : ' || SQLCODE || ' , SQLERRM :' || SQLERRM) ; 20 end ; 21 DBMS_OUTPUT.PUT_LINE('過程調用之后:v_a :' || V_A || ' ---- v_b : ' || V_B) ; 22 end ;
(1)執行結果
1 過程調用之前:v_a :10 ---- v_b : 20 2 SQLCODE : -20001 , SQLERRM :ORA-20001: 測試NOCOPY 3 過程調用之后:v_a :10 ---- v_b : 100
我們可以看到,發生了異常,使用了NOCOPY的P_B修改了實際參數的值,而p_a並沒有。
(2)例子:
1 CREATE OR REPLACE PROCEDURE RaiseErrorNoCopy ( -- 創建過程 2 p_Raise IN BOOLEAN, 3 p_ParameterA OUT NOCOPY NUMBER 4 ) AS 5 BEGIN 6 p_ParameterA := 7; -- 修改實際參數的值 7 IF p_Raise THEN 8 RAISE DUP_VAL_ON_INDEX; --p_Raise為TRUE拋出異常 9 ELSE 10 RETURN; 11 END IF; 12 END RaiseErrorNoCopy;
(2)執行結果
1 DECLARE 2 v_Num NUMBER := 1; -- 定義變量 3 BEGIN 4 DBMS_OUTPUT.PUT_LINE('過程調用之前:' || v_Num); 5 RaiseErrorNoCopy(FALSE, v_Num); --調用過程 6 DBMS_OUTPUT.PUT_LINE('過程調用之后: ' || v_Num); 7 DBMS_OUTPUT.PUT_LINE(''); 8 9 v_Num := 2; --修改變量值 10 DBMS_OUTPUT.PUT_LINE('過程調用之前: ' || v_Num); 11 RaiseErrorNoCopy(TRUE, v_Num); --調用過程將會有異常拋出 12 EXCEPTION 13 WHEN OTHERS THEN --處理異常 14 DBMS_OUTPUT.PUT_LINE('過程調用之后: ' || v_Num); 15 END;
我們可以看到,即使發生了異常,還是兩次修改了實際參數的值。
3、使用NOCOPY的一些限制
在某些情況下,編譯器會忽略NOCOPY的存在,參數仍然以傳值的方式進行傳遞,而且也不會產生任何錯誤。記住,NOCOPY只是一種pragma,編譯器沒有責任完全遵守這個提示。在下面幾種情形中,會忽略NOCOPY的存在:
- 實際參數是聯合數組的一個成員。但是,如果實際參數是整個數組,就不受這種約束的限制。
- 使用長度、精度或NOT NULL約束限制的實際參數。
- 實際參數和形式參數都是記錄,並且它們要么被隱式聲明為一個循環變量,要么是使用%ROWTYPE進行聲明的,而且相應字段上的約束又不同。
- 傳遞的實際參數需要進行隱式的數據類型轉換。
- 子程序被包含在進行遠程過程調用(remote procedures call,RPC)中。
【轉】http://blog.csdn.net/rudygao/article/details/24348795