Oracle 第7章 子程序、程序包

 

Oracle 第6章 遊標

Oracle 第8章 觸發器、內置程序包

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子程序,即過程和函數
  • 過程用戶執行特定的任務,函數用於執行任務並返回值
  • 程序包是對相關類型、變量、常量、遊標、異常、過程和函數等對象的封裝
  • 程序包由兩部分組成,即包規範和包主體
  • 使用程序包的優點是:模塊化、更輕鬆的程序設計、信息隱藏、新增功能以及性能更佳

 

Oracle 第6章 遊標

Oracle 第8章 觸發器、內置程序包

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章