Oracle 第7章 子程序、程序包
1、技術目標
- 創建並使用子程序
- 創建並使用程序包
2、什麼是子程序
子程序是已命名的PL/SQL塊,存儲在數據庫中
命名的PL/SQL程序有兩種:
- 存儲過程
- 函數
注意:程序包是存儲過程和函數的集合
與匿名PL/SQL塊相同,子程序由3部分組成:
- 聲明部分
- 可執行部分
- 異常處理部分(可選)
子程序的優點如下:
- 模塊化,將程序分解爲邏輯模塊
- 可重用性,可以被任意數目的程序調用
- 可維護性,簡化維護操作
- 安全性,通過設置權限,使數據更安全
3、存儲過程
存儲過程是用於完成特定任務的子程序,創建語法 爲:
CREATE [OR REPLACE] PROCEDURE
procedure_name [(parameter_list)]
IS | AS
local_declaration
BEGIN
executable_statements
[EXCEPTION
exception_handlers]
END [procedure_name];
語法說明:
procedure_name,爲存儲過程名
parameter_list,爲過程參數列表
local_declaration,爲聲明部分,定義在IS或AS關鍵字後(匿名塊定義在DECLARE後)
executable_statements,爲可執行語句
exception_handlers,爲異常處理程序
注意:建議將過程名追加到END子句後
使用: 創建存儲過程,接收員工編號,查詢出該編號的員工並顯示,未找到則
處理異常並顯示"未找到該編號的僱員",先創建如下存儲過程,
CREATE OR REPLACE PROCEDURE
findEmp(employeeNo NUMBER)
AS
empName VARCHAR2(20);
BEGIN
SELECT eName INTO empName
FROM EMP WHERE empNo = employeeNo;
DBMS_OUTPUT.PUT_LINE('僱員姓名是:'|| empName);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('未找到該編號的僱員');
END findEmp;
/
再調用該存儲過程,語法 爲:
EXECUTE procedure_name (parameters_list);
如,
--調用findEmp存儲過程並傳遞參數9527(僱員編號)
set serveroutput on
EXECUTE findEmp(9527);
4、存儲過程參數的三種模式
- IN(輸入參數),用於接受調用程序的值,爲默認的參數模式
- OUT(輸出參數),用於向調用程序返回值
- IN OUT(輸入輸出參數),用於接受調用程序的值,並向返回更新的值
定義存儲過程參數的語法 爲:
parameter_name [IN | OUT | IN OUT] datatype
[{:= | DEFAULT} expression]
語法說明:
參數IN爲默認模式,如果是OUT或者IN OUT參數,必須給其賦值才能返回,
可以爲IN參數賦默認值,不能爲 OUT、IN OUT參數賦默認值
使用1: 創建帶IN參數的存儲過程,顯示指定項目編號的項目描述,
CREATE OR REPLACE PROCEDURE
itemdesc(itemCode IN VARCHAR2)
IS
vItemdesc VARCHAR2(5);
BEGIN
SELECT itemdesc INTO vItemdesc
FROM ItemFile
WHERE itemcode = itemCode;
DBMS_OUTPUT.PUT_LINE(item_code|| '項目的說明爲'|| vItemdesc);
END;
/
SET SERVEROUTPUT ON
EXECUTE itemdesc('I188');--調用存儲過程
使用2: 創建並使用帶OUT參數的存儲過程,根據輸入
查詢記錄並通過輸出參數返回值,
CREATE OR REPLACE PROCEDURE
test(value1 IN VARCHAR2,
value2 OUT NUMBER)
IS
identity NUMBER;
BEGIN
SELECT itemrate INTO identity
FROM ItemFile
WHERE itemcode = value1;
IF identity < 200 THEN
value2:=100;
END IF;
END;
--使用匿名塊,調用存儲過程
DECLARE
value1 VARCHAR2(5) := 'i202';
value2 NUMBER;
BEGIN
--調用存儲過程test
test (value1, value2);
DBMS_OUTPUT.PUT_LINE('value2 的值爲' || TO_CHAR(value2));
END;
/
使用3: 創建帶IN OUT參數的存儲過程,
CREATE OR REPLACE PROCEDURE
swap(p1 IN OUT NUMBER,
p2 IN OUT NUMBER)
IS
vTemp NUMBER;
BEGIN
--交換兩個參數的值
vTemp := p1;
p1 := p2;
p2 := vTemp;
END;
/
--使用匿名塊,調用存儲過程
SET SERVEROUT ON
DECLARE
num1 NUMBER := 100;
num2 NUMBER := 200;
BEGIN
swap(num1, num2);
DBMS_OUTPUT.PUT_LINE('num1 = ' || num1);
DBMS_OUTPUT.PUT_LINE('num2 = ' || num2);
END;
/
5、爲其他用戶執行存儲過程授權、刪除
存儲過程只有創建者可執行,其他用戶需授權執行,授權語句如下:
--授權給指定用戶
GRANT EXECUTE ON 存儲過程名 TO 用戶名;
--授權給所有用戶
GRANT EXECUTE ON 存儲過程名 TO PUBLIC;
刪除存儲過程的語句 如下:
DROP PROCEDURE 存儲過程名;
6、函數
函數與存儲過程相似,也是已命名程序塊,其主要特徵是必須返回一個值 ,
創建函數時需通過return子句指定函數返回值的類型,
創建函數的語法 爲:
CREATE [OR REPLACE] FUNCTION
function_name [(param1,param2)]
RETURN datatype {IS|AS}
[local_declarations]
BEGIN
executable_statements;
RETURN result;
EXCEPTION
exception_handlers;
END ;
創建函數的限制 有:
- 函數只能接受IN參數,而不能接受IN OUT或OUT參數
- 形參只能使用數據庫類型,不能是PL/SQL類型
- 函數的返回類型也必須是數據庫類型
訪問函數的兩種方式:
- 使用PL/SQL塊
- 使用SQL語句
使用1: 創建一個簡單的函數funHello,
CREATE OR REPLACE FUNCTION funHello
RETURN VARCHAR2 IS
BEGIN
--返回指定類型的值
RETURN '您好,朋友!';
END;
/
--調用函數funHello,使用SQL
select funHello from dual;
使用2: 創建函數itemPriceRange,驗證產品價格是否超出範圍,
CREATE OR REPLACE FUNCTION
itemPriceRange (price NUMBER)
RETURN VARCHAR2 AS
minPrice NUMBER;
maxPrice NUMBER;
BEGIN
SELECT MAX(itemRate), MIN(itemRate)
INTO minPrice, maxPrice
FROM ItemFile;
IF price >= minPrice AND price <= maxPrice THEN
RETURN '輸入的單價介於最低價與最高價之間';
ELSE
RETURN '超出範圍';
END IF;
END;
/
--PL/SQL塊中調用函數
DECLARE
p NUMBER := 300;
msg VARCHAR2(200);
BEGIN
msg := itemPriceRange(p);
DBMS_OUTPUT.PUT_LINE(msg);
END;
/
7、函數的授權、刪除
其他用戶調用函數需獲得授權,授權語句 如下:
GRANT EXECUTE ON funHello TO 用戶名;
刪除函數:
DROP FUNCTION 函數名;
8、使用函數的限制
- 在select語句中調用的任何函數不得修改數據庫表
- 遠程執行或並行執行時,函數不得讀取或寫入程序包中變量的值
- 從select、values或set子句調用的函數可以寫入程序包中的變量,其他子句中的函數不得寫入程序包變量
- 如函數調用執行了update的存儲過程,則該函數不能出現在SQL語句內
9、存儲過程與函數的比較
過程 | 函 數 |
作爲 PL/SQL 語句執行 | 作爲表達式的一部分調用 |
在規格說明中不包含RETURN子句 | 必須在規格說明中包含RETURN子句 |
不返回任何值 |
必須返回單個值 |
可以包含 RETURN 語句, 但是與函數不同, 它不能用於返回值 |
必須包含至少一條 RETURN 語句 |
10、自主事務處理
自主事務處理是由另一個事務處理(主事務處理)啓動的獨立事務處理,
它可以暫停主事務處理並處理過程內的SQL操作,提交或回滾操作,然
後恢復主事務處理
當一個過程調用另一個過程時,在其中任一過程中進行的任何更改在這
兩個過程中都是可見的,任何提交或回滾語句均會影響這兩個過程中的
事務處理
問題: 創建兩個存儲過程P1、P2,P1調用P2
--創建P2
CREATE OR REPLACE PROCEDURE P2
AS
a varchar2(50);
BEGIN
--查詢編號爲V008的賣家地址
select venadd into a
from venderMaster where venCode = 'V008';
--輸出顯示賣家地址
DBMS_OUTPUT.PUT_LINE(a);
--回滾
ROLLBACK;
END;
/
--創建P1
CREATE OR REPLACE PROCEDURE P1
AS
b varchar2(50);
BEGIN
--修改編號爲V008的賣家地址
update venderMaster set venadd = '科園1街'
where venCode = 'V008'
--調用存儲過程P2顯示賣家地址
P2();
--查詢並顯示編號爲V008的賣家地址
select venadd into b
from venderMaster where venCode = 'V008';
--輸出顯示賣家地址
DBMS_OUTPUT.PUT_LINE(b);
END;
/
輸出結果 (假設編號爲V008的賣家原地址爲'創業1路'):
科園1街(注:由P2輸出)
創業1路(注:由P1輸出)
問題描述: 當P2執行回滾操作時,P1也會受影響,說明事務處理可以
跨存儲過程執行
解決方案:
爲防止一個過程影響其他過程,可將存儲過程設置爲自主事務 ,確保
P1中進行的更改(包括提交或回滾)不影響P2,設置自主事務可使用編
譯關鍵字AUTONOMOUS_TRANSACTION,該關鍵字通知PL/SQL編譯
器將過程、函數或PL/SQL塊標記爲自主事務,修改P2,在聲明中包含如
下代碼:
PRAGMA AUTONOMOUS_TRANSACTION,
--創建P2
CREATE OR REPLACE PROCEDURE P2
AS
a varchar2(50);
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
--查詢編號爲V008的賣家地址
select venadd into a
from venderMaster where venCode = 'V008';
--輸出顯示賣家地址
DBMS_OUTPUT.PUT_LINE(a);
--回滾
ROLLBACK;
END;
/
再次執行P1,輸出結果 爲(假設賣家V008原地址爲'創業1路'):
創業1路(注:由P2輸出)
科園1街(注:由P1輸出)
自主事務處理具有如下特徵 :
- 與主事務處理的狀態無關
- 提交或回滾操作不影響主事務處理
- 自主事務處理的結果對其他事務是可見的
- 能夠啓動其他自主事務處理
11、程序包
程序包是數據庫對象的一種,是對相關過程、函數、變量、遊標和異常
等對象的封裝,程序包如下兩部分組成:
- 規範,聲明程序包中公共對象,包括類型、變量、常量、異常、遊標規範和子程序規範等
- 主體,聲明程序包私有對象和實現在包規範中聲明的子程序和遊標
12、程序包規範
規範包含一些應用程序可見的公共對象和類型的聲明,是與應用程序
的接口,規範包含了應用程序所需的程序包資源,如果規範只聲明瞭
類型、常量、變量和異常,則不需要主體。子程序、遊標必須有程序
主體,創建規範的語法 爲:
CREATE [OR REPLACE] PACKAGE package_name
IS|AS
[public type and item declarations]
[subprogram specifications]
END [package_name];
語法說明:
package_name,爲包名
public type and item declarations,聲明類型、常量、變量、異常和遊標等
subprogram specifications,聲明PL/SQL子程序
在規範中聲明的項,在包之外也可使用,這種項稱爲"公共對象 ",
使用: 創建包packTemp,在此包中聲明子程序orderProc和orderFun,
程序的實現將在包主體中定義,
CREATE OR REPLACE PACKAGE packTemp IS
PROCEDURE orderProc(orderNo varchar2);
FUNCTION orderFun(orderNos varchar2) RETURN varchar2;
END parcTemp;
13、包主體
包主體包含在規範中聲明的每個遊標和子程序的具體實現,私有
聲明也可包括在主體中,創建主題的語法 爲:
CREATE [OR REPLACE] PACKAGE BODY package_name
IS | AS
[public type and item declarations]
[subprogram bodies]
[BEGIN
initialization_statements]
END [package_name];
語法說明:
package_name,爲包名稱
public type and item declarations,爲聲明變量、常量、遊標、異常或類型
subprogram bodies,爲定義公共和私有PL/SQL子程序
使用1: 爲packTemp包創建主體,
CREATE OR REPLACE PACKAGE BODY packTemp AS
--定義存儲過程orderProc
PROCEDURE orderProc(orno VARCHAR2) IS
stat CHAR(1);
BEGIN
SELECT ostatus INTO stat FROM order_master
WHERE orderno = orno;
……
END order_proc;
--定義函數orderFun
FUNCTION orderFun(ornos VARCHAR2)
RETURN VARCHAR2
IS
icode VARCHAR2(5);
ocode VARCHAR2(5);
BEGIN
……
END order_fun;
END pack_me;
/
注意:在創建包規範或主體時發生編譯錯誤,可輸入SHOW ERRORS
命令查看錯誤報告
如需引用包規範中聲明的類型、對象、子程序,使用如下語法:
package_name.type_name;
package_name.object_name;
package_name.subprogram_name;
使用2: 執行packTemp包中的存儲過程orderProc,
EXECUTE packTemp.orderProc('參數');
14、使用包的優勢
- 模塊化,可封裝類型、對象和子程序
- 更輕鬆的應用程序設計,可在沒有主體情況下先編寫規範
- 信息隱藏,利用私有對象和公有對象
- 新增功能,可在同一包中創建同名存儲過程,過程的參數數量或數據類型可不同,允許創建在所有存儲過程和函數中都能使用的全局變量和遊標
- 性能更佳,首次調用包中的子程序,整個包均加載到內存,後續調用無需
- 磁盤I/O操作,另外,更改已打包函數的定義無需重新編譯調用
15、包中的遊標
包中可定義和使用遊標,遊標定義分爲:
- 遊標規範
- 遊標主體
注意:包規範中聲明遊標規範時必須用RETURN指定遊標返回類型
RETURN子句指示從遊標獲取並返回的數據元素,數據元素由遊標
主體中的select語句確定,遊標規範必須包含程序中所使用遊標所
需的所有信息,因此需要返回數據類型,return子句可由兩種數據
類型結構之一組成:
- 使用%ROWTYPE屬性根據數據庫表定義的記錄
- 根據程序員定義的記錄類型的記錄
使用: 在curPack包中爲orderMaster表創建和定義ordCur遊標,
創建存儲過程ordPro檢索vcode,
--定義規範
CREATE OR REPLACE PACKAGE curPack IS
--遊標規範
CURSOR ordCur(vcode VARCHAR2)
RETURN orderMaster%ROWTYPE; --返回類型
--存儲過程ordPro
PROCEDURE ordPro(vcode VARCHAR2);
END cur_pack;
--定義主體
CREATE OR REPLACE PACKAGE BODY curPack AS
--遊標主體
CURSOR ordCur(vcode VARCHAR2)
RETURN orderMaster%ROWTYPE --返回類型
IS SELECT * FROM orderMaster WHERE venCode = vcode;
--存儲過程
PROCEDURE ordPro(vcode VARCHAR2) IS
orRec orderMaster%ROWTYPE;
BEGIN
--打開遊標
OPEN ordCur(vcode);
LOOP
FETCH ordCur INTO orRec;
EXIT WHEN ordCur%NOTFOUND;
DBMS_OUTPUT.PUT_LIne(’ 返回的值爲 ' || orRec.orderNo);
END LOOP;
END ordPro;
END curPack;
/
16、關於程序和程序包
子程序和程序包爲數據庫存儲的對象,Oracle會在數據字典中存儲對象的信息,
查詢USER_OBJECTS視圖,可獲取子程序和包信息
使用1: 查詢存儲過程、函數、包的信息,
SELECT object_name, object_type
FROM USER_OBJECTS
WHERE object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY');
USER_SOURCE視圖存儲子程序和程序包的源代碼,
使用2: 查詢存儲過程TEST的源代碼,
SELECT line, text FROM USER_SOURCE
WHERE NAME='TEST';
17、總結
- 子程序是命名的 PL/SQL 塊,可帶參數並可在需要時隨時調用
- 有兩種類型的PL/SQL子程序,即過程和函數
- 過程用戶執行特定的任務,函數用於執行任務並返回值
- 程序包是對相關類型、變量、常量、遊標、異常、過程和函數等對象的封裝
- 程序包由兩部分組成,即包規範和包主體
- 使用程序包的優點是:模塊化、更輕鬆的程序設計、信息隱藏、新增功能以及性能更佳