1.1 創建和刪除存儲過程
創建存儲過程,需要有CREATE PROCEDURE 或 CREATE ANY PROCEDURE的系統權限.
基本語法如下:
CREATE OR REPLACE PROCEDURE SP_NAME( PM_NAME [IN/OUT/IN OUT] PM_TYPE...)
{ AS}
[說明(變量定義)部分]
BEGIN
可執行部分
[ EXCEPTION]
錯誤處理部分
END [SP_NAME] ;
參數部分用於定義若干個參數(若沒有參數,可以忽略)。參數有三種形式: IN / OUT 和 IN OUT, 默認為IN。
關鍵字AS 也可以寫成 IS.
刪除stored procedure 的用戶必須是其owner或者具有 DROP ANY PROCEDURE的權限。
語法如下:
DROP PROCEDURE SP_NAME;
如果要 重新compile 1個 stored procedure. 則用戶必須是其owner或者擁有 ALTER ANY PROCEDURE的權限
語法如下:
ALTER PORCEDURE SP_NAME COMPILE;
執行 stored procedure 的用戶必須是其owner 或者具有EXECUTE ANY PROCEDURE 的權限。
方法1:
EXECUTE 模式名.SP_NAME[(PARAMETER..];
方法2:
BEGIN
模式名.SP_NAME[(PARAMETER..]
END;
如果是調用本帳戶下的存儲過程, 則模式名可以省略。 要調用其他帳戶編寫的存儲過程,則模式明必須添加。
例1:
步驟1: 登錄SCOTT帳號 (默認密碼是tiger)
步驟2: 創建1個返回雇員總人數的stored procedure

注1: 每一條語句都必須用;結尾,否則編譯出錯。
注2: 變量名不能用@字符開頭,否則編譯出錯~
注3: 變量從select 語句復制賦值應該 用 select field1, field2 into v_va1,v_val2 from tb_name.
而不能使用Sybase的 select v_val1 = field1, v_val2 = field2 from tb_name.
步驟3:執行存儲過程。

例2:
循環列出,emp表中所有員工name, 並調用上面的存儲過程顯示總人數。
步驟1: 編寫Stored procedure 如下圖

注1: cursor 也必須定義在as 與 begin end之間。
注2 : 執行另1個stored procedure, 直接輸入sp名字加";" 符號就可以。
步驟2: 執行stored procedure.

1.2 參數傳遞
Oracle stored procedure的參數有3種類型如下:
參數類型 說明
IN 定義1個輸入參數變量,用於傳遞參數給stored procedure
OUT 定義1個輸出參數變量,用於從stored procedure中獲取數據
IN OUT 定義1個輸入輸出參數,同上擁有以上兩個功能。
用法如下:
PM_name IN DATA_type DEFAULT default_value;
定義1個輸入參數變量,用於傳遞參數給stored procedure.在調用sp時,主程序的實際參數可以是常量,有值變量或表達式等。Default關鍵字為可選項,用來設定參數的默認值。如果在調用stored procedure時不指明參數,則該參數變量取默認值。 在存儲過程中,輸入變量用於接受主程序傳遞的值, 但不能對其進行賦值。
PM_name OUT DATA_type;
定義1個輸出參數變量,用於從stored procedure中獲取數據,即變量從存儲過程中返回值給程序。
在調用sp時,主程序傳給sp的輸出參數只能是1個變量,不能是常量或者表達式。 在sp中,參數變量只能被賦值,而不能將其的值賦予其他對象,在sp中必須給輸出變量賦值1次。
PM_name IN OUT DATA_type DEFAULT default_value;
定義1個輸入輸出變量,同時具有以上功能。在調用sp時,主程序傳給sp的實際參數只能是變量,而不能是常量或表達式。Default關鍵字同樣是可選項,用來設定參數的默認值。 在sp內,變量接受主程序傳遞的值,同時可以參加賦值運算,也可以對其賦值。在sp內至少對其賦值 一次。
如果省略 IN, OUT 或 IN OUT關鍵字,則默認是 IN.
例3:
編寫1個給員工增加工資的stored procedure CHANGE_SALARY 通過對IN類型的參數傳遞 要增加工資的雇員編號和增加的工資額。
步驟1: 編寫sp如下圖:

步驟2: 執行sp如下圖

可以見到7788的工資由3000 變成3020
參數傳遞的個數和順序應該與定義的一致,如果順序不一致也可以采用如下方法:
EXECUTE USP_CHANGE_SALARY(p_raise=>20, p_empno=>7788);
例4:
使用OUT類型參數返回stored procedure的結果。
步驟1: 分別編寫兩個sp, 其中USP_GET_EMP_COUNT通過使用 OUT類型參數,將值傳給另1個sp USP_SHOW_EMP_COUNT.

步驟2:執行sp
說明: 在存儲過程中定義了OUT類型的參數P_TOTAL, 在主程序調用該存儲過程時,傳遞了參數v_empcount. 在存儲過程的
select .. into ...語句中對p_total進行賦值。賦值結果有v_empcount變量帶回給主程序顯示。
例5:
使用 IN OUT類型的參數,給電話號碼增加區碼。
步驟1:
編寫並編譯如下存儲過程:
步驟2:
輸入以下命令執行該存儲過程:
可以見到下面輸出了加了區號的電話號碼。
注1: Oracle 語句不區分大小寫
注2 :賦值語句請用:= 而不能用=.
1.3 創建和刪除存儲函數
說明: 在存儲過程中定義了OUT類型的參數P_TOTAL, 在主程序調用該存儲過程時,傳遞了參數v_empcount. 在存儲過程的
select .. into ...語句中對p_total進行賦值。賦值結果有v_empcount變量帶回給主程序顯示。
例5:
使用 IN OUT類型的參數,給電話號碼增加區碼。
步驟1:
編寫並編譯如下存儲過程:

步驟2:
輸入以下命令執行該存儲過程:

可以見到下面輸出了加了區號的電話號碼。
注1: Oracle 語句不區分大小寫
注2 :賦值語句請用:= 而不能用=.
創建函數,需要CREATE PROCEDURE 或 CREATE ANY PROCEDURE的系統權限。
CREATE [ OR REPLACE] FUNCTION func_name[(para_name para_type...)]
RETURN Data_type
IS
[declare part]
Begin
main part
RETURN (expression)
[ EXCEPTION
errors handle part]
END [func_name] ;
其中,參數是可選的,但是只能是IN類型(IN關鍵字一般被忽略)。
在定義部分的RETURN 數據類型,用來表示函數的數據類型,也就是返回值的數據類型,不可忽略。
在可執行部分的Return(表達式),用來生成函數的返回值,其表達式的類型必須跟定義部分說明的
函數類型一致。在函數的執行部分可以有多個Return語句,但只有1個會被執行,一旦執行了return語句,
則函數結束並返回調用環境。
一個存儲函數在不需要時可以換刪除,但刪除的人應該是函數的創建者或者是擁有DROP ANY PROCEDURE系統權限的人,其語法如下:
DROP FUNCTION func_name;
重新編譯一個存儲函數時,編譯的人應該是函數的創建者或者擁有ALTER ANY PROCEDURE系統權限的人,重新編譯一個存儲函數的語法如下:
ALTER PROCEDURE func_name COMPILE;
函數的調用者應該是函數的創建者或者擁有EXECUTE ANY PROCEDURE系統權限的人,或者是被函數的擁有者授予了函數執行權限的用戶,函數的引用和存儲過程不同,函數需要出現在程序體重,可以參加表達式的運算和單獨出現在表達式中,其形式如下:
Varible_name := func_name(para...);
例6:
創建1個通過雇員編號返回雇員名稱的函數GET_EMP_NAME.
步驟1:
編寫並編譯如下存儲過程
步驟2: 測試:

會見到如果輸入的數據在數據庫不存在, 函數會捕捉錯誤信息。
1.5 存儲過程和函數的查看
可以通過對數據字典的訪問來查詢存儲過程或函數的有關信息,如果要查詢當前用戶的存儲過程或函數的源代碼,可以通過對USER_SOURCE數據字典視圖查詢得到。USER_SOURCE的結構如下:

說明: 里面按行存放着過程或函數的腳步,name 是名字, Type表示類型(SP or function),line是行號,Text為對應行的腳本。


例9: 查詢USP_EMP_LIST 存儲過程是否可用

說明: 這里要從視圖user_objects里查詢,VALID表示該存儲過程有效(即是通過了編譯),INVALID表示無效或未通過編譯。當Oracle調用1個無效的存儲過程或函數時,首先試圖對其進行編譯,如果編譯成功則將狀態status設置成valid並執行,否則給出錯誤信息。
當1個存儲過程編譯成功,狀態變成valid,會不會在某些情況下變成invalid呢,結論是完成可能的,比如1個存儲過程里面喲你道的1張表被update或刪除了。存儲過過程就會變成無效invalid,所以要注意存儲過程和函數與其他對象的依賴關系。
如果要檢查stored procs和function的依賴性,可以通過查詢數據字典 USER_DEPENDENCIES來確定,
該視圖結構如下:

說明:
Name為對象名,
Type為對象類型,
referenced_owner 為涉及到的(被依賴的)對象的擁有者賬戶,
referenced_name 被依賴的對象名
referenced_type 被依賴的對象的類型
參考下圖:

還有一種情況需要注意,如果1個用戶A被授予執行屬於用戶B的一個存儲過程,在用戶B的存儲過后層訪問到用戶C的表,用戶B被授予訪問用戶C表的權限,但用戶A沒有授予訪問用戶C的表的權限,那么用戶A調用用戶B的存儲過程是失敗的還是成功的呢?答案是成功的,可以自己實際測試下。
2. 包 (Package)
2.1 包的概念和組成
包是用來存儲相關程序結構的對象,他存儲與數據字典中,包由兩個分離的部分組成,包頭(Package)和包體(Package body).包頭是包的說明部分, 是對外的操作接口,對應用是可見的,包體是包的代碼的實驗部分,對應用來說是不可見的黑盒。
2.2 包中包含的程序結構
包中可以包含的程序結構如下面這張表所示。
包中包含的程序結構
程序結構 說明
過程(Procedure) 帶參數的命名的程序模塊
函數(Function) 帶參數,具有返回值的命名程序模塊
變量(Variable) 存儲變化的量的存儲單元
常量(Constant) 存儲不變的量的存儲單元
游標(Cursor) 用戶定義的數據操作緩存去,可以在執行部分使用。
類型(Type) 用戶定義的新的結構類型
異常(Exception) 在標准包中定義或由用戶自定義,用戶處理程序錯誤。
2.3 包中元素的性質
說明部分可以出現在包的三個不同部分,出現在包頭中的稱為共有元素,出現在包體中的稱為私有元素,出現在包體的過程或函數中的稱為局部變量。 他們的性質有所不同,如下表中表示

在包體中出現的過程或函數,如果需要對外公用,就必須在包頭中說明,包頭中的說明應該和包體中的說明一致。
包有以下優點:
* 包可以方便地將存儲過程和函數組織到一起,每個包又是相互獨立的. 在不同的包中,過程,函數都可以重名,這解決了同1個用戶環境中命名的沖突問題
*包增強了對存儲過程和函數的安全管理,對整個包的訪問權只需要一次授予.
*在同1個會話中,公用變量的值將被保留,知道會話結束.
* 區分了共有過程和私有過程,包的私有過程增強了過程和函數的保密性.
* 包在被首次調用時,就作為1個整體被全部調入內存,減少了多次訪問過程或函數的I/O次數.
2.4 創建包和包體
包由包頭和包體兩部分組成, 包的創建應該先創建包頭部分, 然后再創建包體部分. 創建, 刪除和編譯包的權限同創建,刪除和編譯存儲過程的權限相同.
創建包頭命令如下:
CREATE [OR REPLACE] PACKAGE PK_NAME
IS
共有變量定義
共有類型定義
共有游標定義
共有異常定義
函數說明
過程說明
END;
創建包體命令如下:
CREATE [OR REPLACE] PACKAGE BODY PK_NAME
IS
私有變量定義
私有類型定義
私有游標定義
私有異常定義
函數定義
過程定義
END;
刪除包頭:
Drop Package PKNAME
刪除包頭:
Drop Package body PKNAME
重新編譯包頭:
Alter Package PKNAME COMPILE PACKAGE
重新編譯包體:
Alter Package PKNAME COMPILE PACKAGE BODY
在包頭說明的對象可以在包外調用,調用的方法和調用單獨的過程或函數方法基本相同, 唯一的區別就是要在調用的過程或函數名子前加上包的名字(中間要用".")分隔. 但要注意.不同的session將單獨對包的公用變量進行初始化,所以不同的session會對包的調用屬於不同的應用.
2.5 系統包
Oracle預定義了很多標准的系統包, 這些包可以在應用中直接使用,比如在例子中我們使用的DBMS_OUTPUT包,就是其中1個系統包,而PUT_LINE是該包的一個函數. 常用的系統包如下面表所示:

2.6 包的應用
在sql*plus環境下,包和包體可以分別編譯,也可以一齊編譯. 如果分別編譯,則要先編譯包頭,再編譯包體,如果一齊編譯,則包頭寫在前,包體在后,中間用"/"分隔.
可以將已經存在的SP或function添加到包中,犯法是去掉過程或函數的創建語句 CREATE OR REPLACE部分, 將存儲過程或函數復制到包體中,然后重新編譯即可.
如果需要將私有過程或函數變成共有過程或函數的話, 將過程或函數說明部分復制到到包頭說明部分,然后重新編譯就可以了.
例10: 創建管理雇員信息的包PAK_EMP, 它具有從EMP表獲得雇員信息,修改雇員名稱,修改雇員工資和寫回EMP表的功能.
步驟1:
編寫如下代碼並編譯.
包頭部分:

包體部分:
聲明部分及 SHOW_DETAIL:

GET_EMP:

SAVE_EMP:

CHANGE NAME及 CHANGE_SAL:


步驟3: 顯示雇員信息:

步驟4: 修改雇員工資:

步驟5: 將雇員信息寫入emp表:

說明: row_emp作為包PAK_EMP的私有變量,只能被包里面的對象所訪問.