Oracle創建存儲過程實例

認識存儲過程和函數
存儲過程和函數也是一種PL/SQL塊,是存入數據庫的PL/SQL塊。但存儲過程和函數不同於已經介紹過的PL/SQL程序,我們通常把PL/SQL程序稱爲無名塊,而存儲過程和函數是以命名的方式存儲於數據庫中的。和PL/SQL程序相比,存儲過程有很多優點,具體歸納如下:
* 存儲過程和函數以命名的數據庫對象形式存儲於數據庫當中。存儲在數據庫中的優點是很明顯的,因爲代碼不保存在本地,用戶可以在任何客戶機上登錄到數據庫,並調用或修改代碼。
* 存儲過程和函數可由數據庫提供安全保證,要想使用存儲過程和函數,需要有存儲過程和函數的所有者的授權,只有被授權的用戶或創建者本身才能執行存儲過程或調用函數。
* 存儲過程和函數的信息是寫入數據字典的,所以存儲過程可以看作是一個公用模塊,用戶編寫的PL/SQL程序或其他存儲過程都可以調用它(但存儲過程和函數不能調用PL/SQL程序)。一個重複使用的功能,可以設計成爲存儲過程,比如:顯示一張工資統計表,可以設計成爲存儲過程;一個經常調用的計算,可以設計成爲存儲函數;根據僱員編號返回僱員的姓名,可以設計成存儲函數。
* 像其他高級語言的過程和函數一樣,可以傳遞參數給存儲過程或函數,參數的傳遞也有多種方式。存儲過程可以有返回值,也可以沒有返回值,存儲過程的返回值必須通過參數帶回;函數有一定的數據類型,像其他的標準函數一樣,我們可以通過對函數名的調用返回函數值。
   存儲過程和函數需要進行編譯,以排除語法錯誤,只有編譯通過才能調用。
創建和刪除存儲過程
創建存儲過程,需要有CREATE PROCEDURE或CREATE ANY PROCEDURE的系統權限。該權限可由系統管理員授予。創建一個存儲過程的基本語句如下:
CREATE [OR REPLACE] PROCEDURE 存儲過程名[(參數[IN|OUT|IN OUT] 數據類型…)]
{AS|IS}
[說明部分]
BEGIN
可執行部分
[EXCEPTION
錯誤處理部分]
END [過程名];
其中:
可選關鍵字OR REPLACE 表示如果存儲過程已經存在,則用新的存儲過程覆蓋,通常用於存儲過程的重建。
參數部分用於定義多個參數(如果沒有參數,就可以省略)。參數有三種形式:IN、OUT和IN OUT。如果沒有指明參數的形式,則默認爲IN。
關鍵字AS也可以寫成IS,後跟過程的說明部分,可以在此定義過程的局部變量。
編寫存儲過程可以使用任何文本編輯器或直接在SQL*Plus環境下進行,編寫好的存儲過程必須要在SQL*Plus環境下進行編譯,生成編譯代碼,原代碼和編譯代碼在編譯過程中都會被存入數據庫。編譯成功的存儲過程就可以在Oracle環境下進行調用了。
一個存儲過程在不需要時可以刪除。刪除存儲過程的人是過程的創建者或者擁有DROP ANY PROCEDURE系統權限的人。刪除存儲過程的語法如下:
DROP PROCEDURE 存儲過程名;
如果要重新編譯一個存儲過程,則只能是過程的創建者或者擁有ALTER ANY PROCEDURE系統權限的人。語法如下:
ALTER PROCEDURE 存儲過程名 COMPILE;
執行(或調用)存儲過程的人是過程的創建者或是擁有EXECUTE ANY PROCEDURE系統權限的人或是被擁有者授予EXECUTE權限的人。執行的方法如下:
方法1:
EXECUTE 模式名.存儲過程名[(參數…)];
方法2:
BEGIN
模式名.存儲過程名[(參數…)];
END;
傳遞的參數必須與定義的參數類型、個數和順序一致(如果參數定義了默認值,則調用時可以省略參數)。參數可以是變量、常量或表達式,用法參見下一節。
如果是調用本賬戶下的存儲過程,則模式名可以省略。要調用其他賬戶編寫的存儲過程,則模式名必須要添加。
以下是一個生成和調用簡單存儲過程的訓練。注意要事先授予創建存儲過程的權限。
【訓練1】  創建一個顯示僱員總人數的存儲過程。
步驟1:登錄SCOTT賬戶(或學生個人賬戶)。
步驟2:在SQL*Plus輸入區中,輸入以下存儲過程:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE EMP_COUNT   
  2. AS  
  3. V_TOTAL NUMBER(10);   
  4. BEGIN  
  5.  SELECT COUNT(*) INTO V_TOTAL FROM EMP;   
  6.  DBMS_OUTPUT.PUT_LINE(’僱員總人數爲:’||V_TOTAL);   
  7. END;  

  1. CREATE OR REPLACE PROCEDURE EMP_COUNT  
  2. AS  
  3. V_TOTAL NUMBER(10);  
  4. BEGIN  
  5.  SELECT COUNT(*) INTO V_TOTAL FROM EMP;  
  6.  DBMS_OUTPUT.PUT_LINE(’僱員總人數爲:’||V_TOTAL);  
  7. END;  



步驟3:按“執行”按鈕進行編譯。
如果存在錯誤,就會顯示:
警告: 創建的過程帶有編譯錯誤。
如果存在錯誤,對腳本進行修改,直到沒有錯誤產生。
如果編譯結果正確,將顯示:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟4:調用存儲過程,在輸入區中輸入以下語句並執行:
Sql代碼 複製代碼
  1. EXECUTE EMP_COUNT;  

  1. EXECUTE EMP_COUNT;  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員總人數爲:14   
  2.         PL/SQL 過程已成功完成。  

  1. 僱員總人數爲:14  
  2.         PL/SQL 過程已成功完成。  



說明:在該訓練中,V_TOTAL變量是存儲過程定義的局部變量,用於接收查詢到的僱員總人數。
注意:在SQL*Plus中輸入存儲過程,按“執行”按鈕是進行編譯,不是執行存儲過程。
  如果在存儲過程中引用了其他用戶的對象,比如表,則必須有其他用戶授予的對象訪問權限。一個存儲過程一旦編譯成功,就可以由其他用戶或程序來引用。但存儲過程或函數的所有者必須授予其他用戶執行該過程的權限。
存儲過程沒有參數,在調用時,直接寫過程名即可。
【訓練2】  在PL/SQL程序中調用存儲過程。
步驟1:登錄SCOTT賬戶。
步驟2:授權STUDENT賬戶使用該存儲過程,即在SQL*Plus輸入區中,輸入以下的命令:
Sql代碼 複製代碼
  1. GRANT EXECUTE ON EMP_COUNT TO STUDENT  

  1. GRANT EXECUTE ON EMP_COUNT TO STUDENT  



Sql代碼 複製代碼
  1. 授權成功。  

  1. 授權成功。  



步驟3:登錄STUDENT賬戶,在SQL*Plus輸入區中輸入以下程序:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2.         BEGIN  
  3.         SCOTT.EMP_COUNT;   
  4.         END;  

  1. SET SERVEROUTPUT ON  
  2.         BEGIN  
  3.         SCOTT.EMP_COUNT;  
  4.         END;  



步驟4:執行以上程序,結果爲:
Sql代碼 複製代碼
  1. 僱員總人數爲:14   
  2.         PL/SQL 過程已成功完成。   

  1. 僱員總人數爲:14  
  2.         PL/SQL 過程已成功完成。   



  說明:在本例中,存儲過程是由SCOTT賬戶創建的,STUDEN賬戶獲得SCOTT賬戶的授權後,才能調用該存儲過程。
  注意:在程序中調用存儲過程,使用了第二種語法。
【訓練3】  編寫顯示僱員信息的存儲過程EMP_LIST,並引用EMP_COUNT存儲過程。
步驟1:在SQL*Plus輸入區中輸入並編譯以下存儲過程:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE EMP_LIST   
  2.         AS  
  3.          CURSOR emp_cursor IS    
  4.         SELECT empno,ename FROM emp;   
  5.         BEGIN  
  6. FOR Emp_record IN emp_cursor LOOP      
  7.     DBMS_OUTPUT.PUT_LINE(Emp_record.empno||Emp_record.ename);   
  8.         END LOOP;   
  9.         EMP_COUNT;   
  10.         END;  

  1. CREATE OR REPLACE PROCEDURE EMP_LIST  
  2.         AS  
  3.          CURSOR emp_cursor IS   
  4.         SELECT empno,ename FROM emp;  
  5.         BEGIN  
  6. FOR Emp_record IN emp_cursor LOOP     
  7.     DBMS_OUTPUT.PUT_LINE(Emp_record.empno||Emp_record.ename);  
  8.         END LOOP;  
  9.         EMP_COUNT;  
  10.         END;  



執行結果:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟2:調用存儲過程,在輸入區中輸入以下語句並執行:
Sql代碼 複製代碼
  1. EXECUTE EMP_LIST  

  1. EXECUTE EMP_LIST  



顯示結果爲:
Sql代碼 複製代碼
  1. 7369SMITH   
  2. 7499ALLEN   
  3. 7521WARD   
  4. 7566JONES   
  5.             執行結果:   
  6.         僱員總人數爲:14   
  7.         PL/SQL 過程已成功完成。  

  1. 7369SMITH  
  2. 7499ALLEN  
  3. 7521WARD  
  4. 7566JONES  
  5.             執行結果:  
  6.         僱員總人數爲:14  
  7.         PL/SQL 過程已成功完成。  



說明:以上的EMP_LIST存儲過程中定義並使用了遊標,用來循環顯示所有僱員的信息。然後調用已經成功編譯的存儲過程EMP_COUNT,用來附加顯示僱員總人數。通過EXECUTE命令來執行EMP_LIST存儲過程。
【練習1】編寫顯示部門信息的存儲過程DEPT_LIST,要求統計出部門個數。
參數傳遞
參數的作用是向存儲過程傳遞數據,或從存儲過程獲得返回結果。正確的使用參數可以大大增加存儲過程的靈活性和通用性。
參數的類型有三種,如下所示。
Sql代碼 複製代碼
  1. IN  定義一個輸入參數變量,用於傳遞參數給存儲過程   
  2. OUT 定義一個輸出參數變量,用於從存儲過程獲取數據   
  3. IN OUT  定義一個輸入、輸出參數變量,兼有以上兩者的功能  

  1. IN  定義一個輸入參數變量,用於傳遞參數給存儲過程  
  2. OUT 定義一個輸出參數變量,用於從存儲過程獲取數據  
  3. IN OUT  定義一個輸入、輸出參數變量,兼有以上兩者的功能  



參數的定義形式和作用如下:
參數名 IN 數據類型 DEFAULT 值;
定義一個輸入參數變量,用於傳遞參數給存儲過程。在調用存儲過程時,主程序的實際參數可以是常量、有值變量或表達式等。DEFAULT 關鍵字爲可選項,用來設定參數的默認值。如果在調用存儲過程時不指明參數,則參數變量取默認值。在存儲過程中,輸入變量接收主程序傳遞的值,但不能對其進行賦值。
參數名 OUT 數據類型;
定義一個輸出參數變量,用於從存儲過程獲取數據,即變量從存儲過程中返回值給主程序。
在調用存儲過程時,主程序的實際參數只能是一個變量,而不能是常量或表達式。在存儲過程中,參數變量只能被賦值而不能將其用於賦值,在存儲過程中必須給輸出變量至少賦值一次。
參數名 IN OUT 數據類型 DEFAULT 值;
定義一個輸入、輸出參數變量,兼有以上兩者的功能。在調用存儲過程時,主程序的實際參數只能是一個變量,而不能是常量或表達式。DEFAULT 關鍵字爲可選項,用來設定參數的默認值。在存儲過程中,變量接收主程序傳遞的值,同時可以參加賦值運算,也可以對其進行賦值。在存儲過程中必須給變量至少賦值一次。
如果省略IN、OUT或IN OUT,則默認模式是IN。
【訓練1】  編寫給僱員增加工資的存儲過程CHANGE_SALARY,通過IN類型的參數傳遞要增加工資的僱員編號和增加的工資額。
步驟1:登錄SCOTT賬戶。
  步驟2:在SQL*Plus輸入區中輸入以下存儲過程並執行:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE CHANGE_SALARY(P_EMPNO IN NUMBER DEFAULT 7788,P_RAISE NUMBER DEFAULT 10)   
  2.         AS  
  3.          V_ENAME VARCHAR2(10);   
  4. V_SAL NUMBER(5);   
  5.         BEGIN  
  6.         SELECT ENAME,SAL INTO V_ENAME,V_SAL FROM EMP WHERE EMPNO=P_EMPNO;   
  7.          UPDATE EMP SET SAL=SAL+P_RAISE WHERE EMPNO=P_EMPNO;   
  8.          DBMS_OUTPUT.PUT_LINE(’僱員’||V_ENAME||’的工資被改爲’||TO_CHAR(V_SAL+P_RAISE));   
  9. COMMIT;   
  10.         EXCEPTION   
  11.          WHEN OTHERS THEN  
  12.         DBMS_OUTPUT.PUT_LINE(’發生錯誤,修改失敗!’);   
  13.         ROLLBACK;   
  14.         END;  

  1. CREATE OR REPLACE PROCEDURE CHANGE_SALARY(P_EMPNO IN NUMBER DEFAULT 7788,P_RAISE NUMBER DEFAULT 10)  
  2.         AS  
  3.          V_ENAME VARCHAR2(10);  
  4. V_SAL NUMBER(5);  
  5.         BEGIN  
  6.         SELECT ENAME,SAL INTO V_ENAME,V_SAL FROM EMP WHERE EMPNO=P_EMPNO;  
  7.          UPDATE EMP SET SAL=SAL+P_RAISE WHERE EMPNO=P_EMPNO;  
  8.          DBMS_OUTPUT.PUT_LINE(’僱員’||V_ENAME||‘的工資被改爲’||TO_CHAR(V_SAL+P_RAISE));  
  9. COMMIT;  
  10.         EXCEPTION  
  11.          WHEN OTHERS THEN  
  12.         DBMS_OUTPUT.PUT_LINE(’發生錯誤,修改失敗!’);  
  13.         ROLLBACK;  
  14.         END;  



執行結果爲:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟3:調用存儲過程,在輸入區中輸入以下語句並執行:
Sql代碼 複製代碼
  1. EXECUTE CHANGE_SALARY(7788,80)  

  1. EXECUTE CHANGE_SALARY(7788,80)  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員SCOTT的工資被改爲3080   

  1. 僱員SCOTT的工資被改爲3080   



說明:從執行結果可以看到,僱員SCOTT的工資已由原來的3000改爲3080。
參數的值由調用者傳遞,傳遞的參數的個數、類型和順序應該和定義的一致。如果順序不一致,可以採用以下調用方法。如上例,執行語句可以改爲:
 EXECUTE CHANGE_SALARY(P_RAISE=>80,P_EMPNO=>7788);
  可以看出傳遞參數的順序發生了變化,並且明確指出了參數名和要傳遞的值,=>運算符左側是參數名,右側是參數表達式,這種賦值方法的意義較清楚。
【練習1】創建插入僱員的存儲過程INSERT_EMP,並將僱員編號等作爲參數。
在設計存儲過程的時候,也可以爲參數設定默認值,這樣調用者就可以不傳遞或少傳遞參數了。
【訓練2】  調用存儲過程CHANGE_SALARY,不傳遞參數,使用默認參數值。
在SQL*Plus輸入區中輸入以下命令並執行:
Sql代碼 複製代碼
  1. EXECUTE CHANGE_SALARY  

  1. EXECUTE CHANGE_SALARY  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員SCOTT的工資被改爲3090   

  1. 僱員SCOTT的工資被改爲3090   



說明:在存儲過程的調用中沒有傳遞參數,而是採用了默認值7788和10,即默認僱員號爲7788,增加的工資爲10。
【訓練3】  使用OUT類型的參數返回存儲過程的結果。
步驟1:登錄SCOTT賬戶。
步驟2:在SQL*Plus輸入區中輸入並編譯以下存儲過程:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)   
  2.         AS  
  3.         BEGIN  
  4.         SELECT COUNT(*) INTO P_TOTAL FROM EMP;   
  5.         END;  

  1. CREATE OR REPLACE PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)  
  2.         AS  
  3.         BEGIN  
  4.         SELECT COUNT(*) INTO P_TOTAL FROM EMP;  
  5.         END;  



執行結果爲:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟3:輸入以下程序並執行:
Sql代碼 複製代碼
  1. DECLARE  
  2.         V_EMPCOUNT NUMBER;   
  3.         BEGIN  
  4.         EMP_COUNT(V_EMPCOUNT);   
  5.         DBMS_OUTPUT.PUT_LINE(’僱員總人數爲:’||V_EMPCOUNT);   
  6.         END;  

  1. DECLARE  
  2.         V_EMPCOUNT NUMBER;  
  3.         BEGIN  
  4.         EMP_COUNT(V_EMPCOUNT);  
  5.         DBMS_OUTPUT.PUT_LINE(’僱員總人數爲:’||V_EMPCOUNT);  
  6.         END;  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員總人數爲:14   
  2.         PL/SQL 過程已成功完成。  

  1. 僱員總人數爲:14  
  2.         PL/SQL 過程已成功完成。  



    說明:在存儲過程中定義了OUT類型的參數P_TOTAL,在主程序調用該存儲過程時,傳遞了參數V_EMPCOUNT。在存儲過程中的SELECT…INTO…語句中對P_TOTAL進行賦值,賦值結果由V_EMPCOUNT變量帶回給主程序並顯示。
以上程序要覆蓋同名的EMP_COUNT存儲過程,如果不使用OR REPLACE選項,就會出現以下錯誤:
Sql代碼 複製代碼
  1. ERROR 位於第 1 行:   
  2.         ORA-00955: 名稱已由現有對象使用。  

  1. ERROR 位於第 1 行:  
  2.         ORA-00955: 名稱已由現有對象使用。  



【練習2】創建存儲過程,使用OUT類型參數獲得僱員經理名。
【訓練4】  使用IN OUT類型的參數,給電話號碼增加區碼。
步驟1:登錄SCOTT賬戶。
步驟2:在SQL*Plus輸入區中輸入並編譯以下存儲過程:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE ADD_REGION(P_HPONE_NUM IN OUT VARCHAR2)   
  2.         AS  
  3.         BEGIN  
  4.          P_HPONE_NUM:=’0755-‘||P_HPONE_NUM;   
  5.         END;  

  1. CREATE OR REPLACE PROCEDURE ADD_REGION(P_HPONE_NUM IN OUT VARCHAR2)  
  2.         AS  
  3.         BEGIN  
  4.          P_HPONE_NUM:=’0755-‘||P_HPONE_NUM;  
  5.         END;  



執行結果爲:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟3:輸入以下程序並執行:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2. DECLARE  
  3. V_PHONE_NUM VARCHAR2(15);   
  4. BEGIN  
  5. V_PHONE_NUM:=’26731092’;   
  6. ADD_REGION(V_PHONE_NUM);   
  7. DBMS_OUTPUT.PUT_LINE(’新的電話號碼:’||V_PHONE_NUM);   
  8. END;  

  1. SET SERVEROUTPUT ON  
  2. DECLARE  
  3. V_PHONE_NUM VARCHAR2(15);  
  4. BEGIN  
  5. V_PHONE_NUM:=’26731092’;  
  6. ADD_REGION(V_PHONE_NUM);  
  7. DBMS_OUTPUT.PUT_LINE(’新的電話號碼:’||V_PHONE_NUM);  
  8. END;  



顯示結果爲:
Sql代碼 複製代碼
  1. 新的電話號碼:0755-26731092   
  2.         PL/SQL 過程已成功完成。  

  1. 新的電話號碼:0755-26731092  
  2.         PL/SQL 過程已成功完成。  



說明:變量V_HPONE_NUM既用來向存儲過程傳遞舊電話號碼,也用來向主程序返回新號碼。新的號碼在原來基礎上增加了區號0755和-。
創建和刪除存儲函數
  創建函數,需要有CREATE PROCEDURE或CREATE ANY PROCEDURE的系統權限。該權限可由系統管理員授予。創建存儲函數的語法和創建存儲過程的類似,即
CREATE [OR REPLACE] FUNCTION 函數名[(參數[IN] 數據類型…)]
RETURN 數據類型
{AS|IS}
[說明部分]
BEGIN
可執行部分
RETURN (表達式)
[EXCEPTION
    錯誤處理部分]
END [函數名];
其中,參數是可選的,但只能是IN類型(IN關鍵字可以省略)。
在定義部分的RETURN 數據類型,用來表示函數的數據類型,也就是返回值的類型,此部分不可省略。
在可執行部分的RETURN(表達式),用來生成函數的返回值,其表達式的類型應該和定義部分說明的函數返回值的數據類型一致。在函數的執行部分可以有多個RETURN語句,但只有一個RETURN語句會被執行,一旦執行了RETURN語句,則函數結束並返回調用環境。
一個存儲函數在不需要時可以刪除,但刪除的人應是函數的創建者或者是擁有DROP ANY PROCEDURE系統權限的人。其語法如下:
DROP FUNCTION 函數名;
重新編譯一個存儲函數時,編譯的人應是函數的創建者或者擁有ALTER ANY PROCEDURE系統權限的人。重新編譯一個存儲函數的語法如下:
ALTER PROCEDURE 函數名 COMPILE;
函數的調用者應是函數的創建者或擁有EXECUTE ANY PROCEDURE系統權限的人,或是被函數的擁有者授予了函數執行權限的賬戶。函數的引用和存儲過程不同,函數要出現在程序體中,可以參加表達式的運算或單獨出現在表達式中,其形式如下:
變量名:=函數名(…)
【訓練1】  創建一個通過僱員編號返回僱員名稱的函數GET_EMP_NAME。
步驟1:登錄SCOTT賬戶。
步驟2:在SQL*Plus輸入區中輸入以下存儲函數並編譯:
Sql代碼 複製代碼
  1. CREATE OR REPLACE FUNCTION GET_EMP_NAME(P_EMPNO NUMBER DEFAULT 7788)   
  2.         RETURN VARCHAR2   
  3.         AS  
  4.          V_ENAME VARCHAR2(10);   
  5.         BEGIN  
  6.         ELECT ENAME INTO V_ENAME FROM EMP WHERE EMPNO=P_EMPNO;   
  7. RETURN(V_ENAME);   
  8. EXCEPTION   
  9.  WHEN NO_DATA_FOUND THEN  
  10.   DBMS_OUTPUT.PUT_LINE(’沒有該編號僱員!’);   
  11.   RETURN (NULL);   
  12.  WHEN TOO_MANY_ROWS THEN  
  13.   DBMS_OUTPUT.PUT_LINE(’有重複僱員編號!’);   
  14.   RETURN (NULL);   
  15.  WHEN OTHERS THEN  
  16.   DBMS_OUTPUT.PUT_LINE(’發生其他錯誤!’);   
  17.   RETURN (NULL);   
  18. END;  

  1. CREATE OR REPLACE FUNCTION GET_EMP_NAME(P_EMPNO NUMBER DEFAULT 7788)  
  2.         RETURN VARCHAR2  
  3.         AS  
  4.          V_ENAME VARCHAR2(10);  
  5.         BEGIN  
  6.         ELECT ENAME INTO V_ENAME FROM EMP WHERE EMPNO=P_EMPNO;  
  7. RETURN(V_ENAME);  
  8. EXCEPTION  
  9.  WHEN NO_DATA_FOUND THEN  
  10.   DBMS_OUTPUT.PUT_LINE(’沒有該編號僱員!’);  
  11.   RETURN (NULL);  
  12.  WHEN TOO_MANY_ROWS THEN  
  13.   DBMS_OUTPUT.PUT_LINE(’有重複僱員編號!’);  
  14.   RETURN (NULL);  
  15.  WHEN OTHERS THEN  
  16.   DBMS_OUTPUT.PUT_LINE(’發生其他錯誤!’);  
  17.   RETURN (NULL);  
  18. END;  



步驟3:調用該存儲函數,輸入並執行以下程序:
Sql代碼 複製代碼
  1. BEGIN  
  2.         DBMS_OUTPUT.PUT_LINE(’僱員7369的名稱是:’|| GET_EMP_NAME(7369));   
  3.          DBMS_OUTPUT.PUT_LINE(’僱員7839的名稱是:’|| GET_EMP_NAME(7839));   
  4.         END;  

  1. BEGIN  
  2.         DBMS_OUTPUT.PUT_LINE(’僱員7369的名稱是:’|| GET_EMP_NAME(7369));  
  3.          DBMS_OUTPUT.PUT_LINE(’僱員7839的名稱是:’|| GET_EMP_NAME(7839));  
  4.         END;  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員7369的名稱是:SMITH   
  2.         僱員7839的名稱是:KING   
  3.         PL/SQL 過程已成功完成。  

  1. 僱員7369的名稱是:SMITH  
  2.         僱員7839的名稱是:KING  
  3.         PL/SQL 過程已成功完成。  



說明:函數的調用直接出現在程序的DBMS_OUTPUT.PUT_LINE語句中,作爲字符串表達式的一部分。如果輸入了錯誤的僱員編號,就會在函數的錯誤處理部分輸出錯誤信息。試修改僱員編號,重新運行調用部分。
【練習1】創建一個通過部門編號返回部門名稱的存儲函數GET_DEPT_NAME。
   【練習2】將函數的執行權限授予STUDENT賬戶,然後登錄STUDENT賬戶調用。
存儲過程和函數的查看
可以通過對數據字典的訪問來查詢存儲過程或函數的有關信息,如果要查詢當前用戶的存儲過程或函數的源代碼,可以通過對USER_SOURCE數據字典視圖的查詢得到。USER_SOURCE的結構如下:
Sql代碼 複製代碼
  1. DESCRIBE USER_SOURCE  

  1. DESCRIBE USER_SOURCE  



結果爲:
Sql代碼 複製代碼
  1. 名稱                                      是否爲空? 類型   
  2.         ————————————————————- ————- ———————–  
  3.  NAME                                               VARCHAR2(30)   
  4.  TYPE                                               VARCHAR2(12)   
  5.  LINE                                               NUMBER   
  6.  TEXT                                               VARCHAR2(4000)  

  1. 名稱                                      是否爲空? 類型  
  2.         ————————————————————- ————- ———————–  
  3.  NAME                                               VARCHAR2(30)  
  4.  TYPE                                               VARCHAR2(12)  
  5.  LINE                                               NUMBER  
  6.  TEXT                                               VARCHAR2(4000)  



  說明:裏面按行存放着過程或函數的腳本,NAME是過程或函數名,TYPE 代表類型(PROCEDURE或FUNCTION),LINE是行號,TEXT 爲腳本。
【訓練1】  查詢過程EMP_COUNT的腳本。
在SQL*Plus中輸入並執行如下查詢:
Sql代碼 複製代碼
  1. select TEXT  from user_source WHERE NAME=’EMP_COUNT’;  

  1. select TEXT  from user_source WHERE NAME=‘EMP_COUNT’;  



結果爲:
Sql代碼 複製代碼
  1. TEXT   
  2. ——————————————————————————–  
  3. PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)   
  4. AS  
  5. BEGIN  
  6.  SELECT COUNT(*) INTO P_TOTAL FROM EMP;   
  7. END;  

  1. TEXT  
  2. ——————————————————————————–  
  3. PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)  
  4. AS  
  5. BEGIN  
  6.  SELECT COUNT(*) INTO P_TOTAL FROM EMP;  
  7. END;  



【訓練2】  查詢過程GET_EMP_NAME的參數。
在SQL*Plus中輸入並執行如下查詢:
Sql代碼 複製代碼
  1. DESCRIBE GET_EMP_NAME  

  1. DESCRIBE GET_EMP_NAME  



結果爲:
Sql代碼 複製代碼
  1. FUNCTION GET_EMP_NAME RETURNS VARCHAR2   
  2.         參數名稱            類型          輸入/輸出默認值?   
  3.         —————————————– ———————————– —————– ————-  
  4.         P_EMPNO             NUMBER(4) IN     DEFAULT  

  1. FUNCTION GET_EMP_NAME RETURNS VARCHAR2  
  2.         參數名稱            類型          輸入/輸出默認值?  
  3.         —————————————– ———————————– —————– ————-  
  4.         P_EMPNO             NUMBER(4) IN     DEFAULT  



【訓練3】  在發生編譯錯誤時,顯示錯誤。
Sql代碼 複製代碼
  1. SHOW ERRORS  

  1. SHOW ERRORS  



以下是一段編譯錯誤顯示:
Sql代碼 複製代碼
  1. LINE/COL ERROR   
  2.         ————- —————————————————————–  
  3.         4/2       PL/SQL: SQL Statement ignored   
  4.         4/36      PLS-00201: 必須說明標識符 ’EMPP’  

  1. LINE/COL ERROR  
  2.         ————- —————————————————————–  
  3.         4/2       PL/SQL: SQL Statement ignored  
  4.         4/36      PLS-00201: 必須說明標識符 ’EMPP’  



  說明:查詢一個存儲過程或函數是否是有效狀態(即編譯成功),可以使用數據字典USER_OBJECTS的STATUS列。
【訓練4】  查詢EMP_LIST存儲過程是否可用:
Sql代碼 複製代碼
  1. SELECT STATUS FROM USER_OBJECTS WHERE OBJECT_NAME=’EMP_LIST’;  

  1. SELECT STATUS FROM USER_OBJECTS WHERE OBJECT_NAME=‘EMP_LIST’;  



結果爲:
Sql代碼 複製代碼
  1. STATUS   
  2.         ————  
  3.         VALID  

  1. STATUS  
  2.         ————  
  3.         VALID  



說明:VALID表示該存儲過程有效(即通過編譯),INVALID表示存儲過程無效或需要重新編譯。當Oracle調用一個無效的存儲過程或函數時,首先試圖對其進行編譯,如果編譯成功則將狀態置成VALID並執行,否則給出錯誤信息。
當一個存儲過程編譯成功,狀態變爲VALID,會不會在某些情況下變成INVALID。結論是完全可能的。比如一個存儲過程中包含對錶的查詢,如果表被修改或刪除,存儲過程就會變成無效INVALID。所以要注意存儲過程和函數對其他對象的依賴關係。
如果要檢查存儲過程或函數的依賴性,可以通過查詢數據字典USER_DENPENDENCIES來確定,該表結構如下:
Sql代碼 複製代碼
  1. DESCRIBE USER_DEPENDENCIES;  

  1. DESCRIBE USER_DEPENDENCIES;  



結果:
Sql代碼 複製代碼
  1. 名稱                     是否爲空? 類型   
  2.         ————————————————————– ————- —————————-  
  3.          NAME            NOT NULL   VARCHAR2(30)   
  4.          TYPE                       VARCHAR2(12)   
  5.         REFERENCED_OWNER                                VARCHAR2(30)   
  6.  REFERENCED_NAME                                VARCHAR2(64)   
  7.  REFERENCED_TYPE                                VARCHAR2(12)   
  8. REFERENCED_LINK_NAME                            VARCHAR2(128)   
  9.         SCHEMAID                                        NUMBER   
  10.          DEPENDENCY_TYPE                                VARCHAR2(4)  

  1. 名稱                     是否爲空? 類型  
  2.         ————————————————————– ————- —————————-  
  3.          NAME            NOT NULL   VARCHAR2(30)  
  4.          TYPE                       VARCHAR2(12)  
  5.         REFERENCED_OWNER                                VARCHAR2(30)  
  6.  REFERENCED_NAME                                VARCHAR2(64)  
  7.  REFERENCED_TYPE                                VARCHAR2(12)  
  8. REFERENCED_LINK_NAME                            VARCHAR2(128)  
  9.         SCHEMAID                                        NUMBER  
  10.          DEPENDENCY_TYPE                                VARCHAR2(4)  



  說明:NAME爲實體名,TYPE爲實體類型,REFERENCED_OWNER爲涉及到的實體擁有者賬戶,REFERENCED_NAME爲涉及到的實體名,REFERENCED_TYPE 爲涉及到的實體類型。
【訓練5】  查詢EMP_LIST存儲過程的依賴性。
Sql代碼 複製代碼
  1. SELECT REFERENCED_NAME,REFERENCED_TYPE FROM USER_DEPENDENCIES WHERE NAME=’EMP_LIST’;  

  1. SELECT REFERENCED_NAME,REFERENCED_TYPE FROM USER_DEPENDENCIES WHERE NAME=‘EMP_LIST’;  



執行結果:
Sql代碼 複製代碼
  1. REFERENCED_NAME                                         REFERENCED_TYPE   
  2.         —————————————————————————————— —————————-  
  3. STANDARD                                                PACKAGE   
  4.         SYS_STUB_FOR_PURITY_ANALYSIS                            PACKAGE   
  5.         DBMS_OUTPUT                                                 PACKAGE   
  6.         DBMS_OUTPUT                                             SYNONYM   
  7. DBMS_OUTPUT                      NON-EXISTENT   
  8.         EMP                                                         TABLE  
  9.         EMP_COUNT                                                   PROCEDURE  

  1. REFERENCED_NAME                                         REFERENCED_TYPE  
  2.         —————————————————————————————— —————————-  
  3. STANDARD                                                PACKAGE  
  4.         SYS_STUB_FOR_PURITY_ANALYSIS                            PACKAGE  
  5.         DBMS_OUTPUT                                                 PACKAGE  
  6.         DBMS_OUTPUT                                             SYNONYM  
  7. DBMS_OUTPUT                      NON-EXISTENT  
  8.         EMP                                                         TABLE  
  9.         EMP_COUNT                                                   PROCEDURE  



  說明:可以看出存儲過程EMP_LIST依賴一些系統包、EMP表和EMP_COUNT存儲過程。如果刪除了EMP表或EMP_COUNT存儲過程,EMP_LIST將變成無效。
還有一種情況需要我們注意:如果一個用戶A被授予執行屬於用戶B的一個存儲過程的權限,在用戶B的存儲過程中,訪問到用戶C的表,用戶B被授予訪問用戶C的表的權限,但用戶A沒有被授予訪問用戶C表的權限,那麼用戶A調用用戶B的存儲過程是失敗的還是成功的呢?答案是成功的。如果讀者有興趣,不妨進行一下實際測試。


包的概念和組成
包是用來存儲相關程序結構的對象,它存儲於數據字典中。包由兩個分離的部分組成:包頭(PACKAGE)和包體(PACKAGE BODY)。包頭是包的說明部分,是對外的操作接口,對應用是可見的;包體是包的代碼和實現部分,對應用來說是不可見的黑盒。
包中可以包含的程序結構如下所示。
Sql代碼 複製代碼
  1. 過程(PROCUDURE)   帶參數的命名的程序模塊   
  2. 函數(FUNCTION)    帶參數、具有返回值的命名的程序模塊   
  3. 變量(VARIABLE)    存儲變化的量的存儲單元   
  4. 常量(CONSTANT)    存儲不變的量的存儲單元   
  5. 遊標(CURSOR)  用戶定義的數據操作緩存區,在可執行部分使用   
  6. 類型(TYPE)    用戶定義的新的結構類型   
  7. 異常(EXCEPTION)   在標準包中定義或由用戶自定義,用於處理程序錯誤  

  1. 過程(PROCUDURE)   帶參數的命名的程序模塊  
  2. 函數(FUNCTION)    帶參數、具有返回值的命名的程序模塊  
  3. 變量(VARIABLE)    存儲變化的量的存儲單元  
  4. 常量(CONSTANT)    存儲不變的量的存儲單元  
  5. 遊標(CURSOR)  用戶定義的數據操作緩存區,在可執行部分使用  
  6. 類型(TYPE)    用戶定義的新的結構類型  
  7. 異常(EXCEPTION)   在標準包中定義或由用戶自定義,用於處理程序錯誤  



說明部分可以出現在包的三個不同的部分:出現在包頭中的稱爲公有元素,出現在包體中的稱爲私有元素,出現在包體的過程(或函數)中的稱爲局部變量。它們的性質有所不同,如下所示。
Sql代碼 複製代碼
  1. 公有元素(PUBLIC)    在包頭中說明,在包體中具體定義 在包外可見並可以訪問,對整個應用的全過程有效   
  2. 私有元素(PRIVATE)   在包體的說明部分說明  只能被包內部的其他部分訪問   
  3. 局部變量(LOCAL) 在過程或函數的說明部分說明   只能在定義變量的過程或函數中使用  

  1. 公有元素(PUBLIC)    在包頭中說明,在包體中具體定義 在包外可見並可以訪問,對整個應用的全過程有效  
  2. 私有元素(PRIVATE)   在包體的說明部分說明  只能被包內部的其他部分訪問  
  3. 局部變量(LOCAL) 在過程或函數的說明部分說明   只能在定義變量的過程或函數中使用  



在包體中出現的過程或函數,如果需要對外公用,就必須在包頭中說明,包頭中的說明應該和包體中的說明一致。
包有以下優點:
* 包可以方便地將存儲過程和函數組織到一起,每個包又是相互獨立的。在不同的包中,過程、函數都可以重名,這解決了在同一個用戶環境中命名的衝突問題。
* 包增強了對存儲過程和函數的安全管理,對整個包的訪問權只需一次授予。
  * 在同一個會話中,公用變量的值將被保留,直到會話結束。
* 區分了公有過程和私有過程,包體的私有過程增加了過程和函數的保密性。
* 包在被首次調用時,就作爲一個整體被全部調入內存,減少了多次訪問過程或函數的I/O次數。
創建包和包體
包由包頭和包體兩部分組成,包的創建應該先創建包頭部分,然後創建包體部分。創建、刪除和編譯包的權限同創建、刪除和編譯存儲過程的權限相同。
創建包頭的簡要語句如下:
CREATE [OR REPLACE] PACKAGE 包名
{IS|AS}
公有變量定義
公有類型定義
公有遊標定義
公有異常定義
函數說明
過程說明
END;
創建包體的簡要語法如下:
CREATE [OR REPLACE] PACKAGE BODY 包名
{IS|AS}
私有變量定義
私有類型定義
私有遊標定義
私有異常定義
函數定義
過程定義
END;
包的其他操作命令包括:
刪除包頭:
DROP PACKAGE 包頭名
刪除包體:
DROP PACKAGE BODY 包體名
重新編譯包頭:
ALTER PACKAGE 包名 COMPILE PACKAGE
重新編譯包體:
ALTER PACKAGE 包名 COMPILE PACKAGE BODY
在包頭中說明的對象可以在包外調用,調用的方法和調用單獨的過程或函數的方法基本相同,惟一的區別就是要在調用的過程或函數名前加上包的名字(中間用“.”分隔)。但要注意,不同的會話將單獨對包的公用變量進行初始化,所以不同的會話對包的調用屬於不同的應用。
系統包
Oracle預定義了很多標準的系統包,這些包可以在應用中直接使用,比如在訓練中我們使用的DBMS_OUTPUT包,就是系統包。PUT_LINE是該包的一個函數。常用系統包下所示。
Sql代碼 複製代碼
  1. DBMS_OUTPUT 在SQL*Plus環境下輸出信息   
  2. DBMS_DDL    編譯過程函數和包   
  3. DBMS_SESSION    改變用戶的會話,初始化包等   
  4. DBMS_TRANSACTION    控制數據庫事務   
  5. DBMS_MAIL   連接Oracle*Mail   
  6. DBMS_LOCK   進行復雜的鎖機制管理   
  7. DBMS_ALERT  識別數據庫事件告警   
  8. DBMS_PIPE   通過管道在會話間傳遞信息   
  9. DBMS_JOB    管理Oracle的作業   
  10. DBMS_LOB    操縱大對象   
  11. DBMS_SQL    執行動態SQL語句  

  1. DBMS_OUTPUT 在SQL*Plus環境下輸出信息  
  2. DBMS_DDL    編譯過程函數和包  
  3. DBMS_SESSION    改變用戶的會話,初始化包等  
  4. DBMS_TRANSACTION    控制數據庫事務  
  5. DBMS_MAIL   連接Oracle*Mail  
  6. DBMS_LOCK   進行復雜的鎖機制管理  
  7. DBMS_ALERT  識別數據庫事件告警  
  8. DBMS_PIPE   通過管道在會話間傳遞信息  
  9. DBMS_JOB    管理Oracle的作業  
  10. DBMS_LOB    操縱大對象  
  11. DBMS_SQL    執行動態SQL語句  



包的應用
在SQL*Plus環境下,包和包體可以分別編譯,也可以一起編譯。如果分別編譯,則要先編譯包頭,後編譯包體。如果在一起編譯,則包頭寫在前,包體在後,中間用“/”分隔。
可以將已經存在的存儲過程或函數添加到包中,方法是去掉過程或函數創建語句的CREATE OR REPLACE部分,將存儲過程或函數複製到包體中 ,然後重新編譯即可。
   如果需要將私有過程或函數變成共有過程或函數的話,將過程或函數說明部分複製到包頭說明部分,然後重新編譯就可以了。
【訓練1】  創建管理僱員信息的包EMPLOYE,它具有從EMP表獲得僱員信息,修改僱員名稱,修改僱員工資和寫回EMP表的功能。
步驟1:登錄SCOTT賬戶,輸入以下代碼並編譯:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PACKAGE EMPLOYE –包頭部分   
  2.         IS  
  3.  PROCEDURE SHOW_DETAIL;    
  4.  PROCEDURE GET_EMPLOYE(P_EMPNO NUMBER);    
  5.  PROCEDURE SAVE_EMPLOYE;    
  6.  PROCEDURE CHANGE_NAME(P_NEWNAME VARCHAR2);    
  7. PROCEDURE CHANGE_SAL(P_NEWSAL NUMBER);    
  8.         END EMPLOYE;   
  9.         /   
  10.         CREATE OR REPLACE PACKAGE BODY EMPLOYE –包體部分   
  11.         IS  
  12.  EMPLOYE EMP%ROWTYPE;   
  13.         ————– 顯示僱員信息 —————  
  14.         PROCEDURE SHOW_DETAIL   
  15.         AS  
  16.         BEGIN  
  17. DBMS_OUTPUT.PUT_LINE(‘—– 僱員信息 —–’);     
  18.         DBMS_OUTPUT.PUT_LINE(’僱員編號:’||EMPLOYE.EMPNO);   
  19.         DBMS_OUTPUT.PUT_LINE(’僱員名稱:’||EMPLOYE.ENAME);   
  20.           DBMS_OUTPUT.PUT_LINE(’僱員職務:’||EMPLOYE.JOB);   
  21.          DBMS_OUTPUT.PUT_LINE(’僱員工資:’||EMPLOYE.SAL);   
  22.          DBMS_OUTPUT.PUT_LINE(’部門編號:’||EMPLOYE.DEPTNO);   
  23.         END SHOW_DETAIL;   
  24. —————– 從EMP表取得一個僱員 ——————–  
  25.          PROCEDURE GET_EMPLOYE(P_EMPNO NUMBER)   
  26.         AS  
  27.         BEGIN  
  28.         SELECT * INTO EMPLOYE FROM EMP WHERE    EMPNO=P_EMPNO;   
  29.         DBMS_OUTPUT.PUT_LINE(’獲取僱員’||EMPLOYE.ENAME||’信息成功’);   
  30.          EXCEPTION   
  31.          WHEN OTHERS THEN  
  32.            DBMS_OUTPUT.PUT_LINE(’獲取僱員信息發生錯誤!’);   
  33.         END GET_EMPLOYE;   
  34. ———————- 保存僱員到EMP表 ————————–  
  35.         PROCEDURE SAVE_EMPLOYE   
  36.         AS  
  37.         BEGIN  
  38.         UPDATE EMP SET ENAME=EMPLOYE.ENAME, SAL=EMPLOYE.SAL WHERE EMPNO=   
  39.     EMPLOYE.EMPNO;   
  40.      DBMS_OUTPUT.PUT_LINE(’僱員信息保存完成!’);   
  41.         END SAVE_EMPLOYE;   
  42. —————————- 修改僱員名稱 ——————————  
  43.         PROCEDURE CHANGE_NAME(P_NEWNAME VARCHAR2)   
  44.          AS  
  45.         BEGIN  
  46.          EMPLOYE.ENAME:=P_NEWNAME;   
  47.          DBMS_OUTPUT.PUT_LINE(’修改名稱完成!’);   
  48.         END CHANGE_NAME;   
  49. —————————- 修改僱員工資 ————————–  
  50.         PROCEDURE CHANGE_SAL(P_NEWSAL NUMBER)   
  51.         AS  
  52.         BEGIN  
  53.          EMPLOYE.SAL:=P_NEWSAL;   
  54.          DBMS_OUTPUT.PUT_LINE(’修改工資完成!’);   
  55.         END CHANGE_SAL;   
  56.         END EMPLOYE;  

  1. CREATE OR REPLACE PACKAGE EMPLOYE –包頭部分   
  2.         IS  
  3.  PROCEDURE SHOW_DETAIL;   
  4.  PROCEDURE GET_EMPLOYE(P_EMPNO NUMBER);   
  5.  PROCEDURE SAVE_EMPLOYE;   
  6.  PROCEDURE CHANGE_NAME(P_NEWNAME VARCHAR2);   
  7. PROCEDURE CHANGE_SAL(P_NEWSAL NUMBER);   
  8.         END EMPLOYE;  
  9.         /  
  10.         CREATE OR REPLACE PACKAGE BODY EMPLOYE –包體部分   
  11.         IS  
  12.  EMPLOYE EMP%ROWTYPE;  
  13.         ————– 顯示僱員信息 —————  
  14.         PROCEDURE SHOW_DETAIL  
  15.         AS  
  16.         BEGIN  
  17. DBMS_OUTPUT.PUT_LINE(‘—– 僱員信息 —–’);     
  18.         DBMS_OUTPUT.PUT_LINE(’僱員編號:’||EMPLOYE.EMPNO);  
  19.         DBMS_OUTPUT.PUT_LINE(’僱員名稱:’||EMPLOYE.ENAME);  
  20.           DBMS_OUTPUT.PUT_LINE(’僱員職務:’||EMPLOYE.JOB);  
  21.          DBMS_OUTPUT.PUT_LINE(’僱員工資:’||EMPLOYE.SAL);  
  22.          DBMS_OUTPUT.PUT_LINE(’部門編號:’||EMPLOYE.DEPTNO);  
  23.         END SHOW_DETAIL;  
  24. —————– 從EMP表取得一個僱員 ——————–  
  25.          PROCEDURE GET_EMPLOYE(P_EMPNO NUMBER)  
  26.         AS  
  27.         BEGIN  
  28.         SELECT * INTO EMPLOYE FROM EMP WHERE    EMPNO=P_EMPNO;  
  29.         DBMS_OUTPUT.PUT_LINE(’獲取僱員’||EMPLOYE.ENAME||‘信息成功’);  
  30.          EXCEPTION  
  31.          WHEN OTHERS THEN  
  32.            DBMS_OUTPUT.PUT_LINE(’獲取僱員信息發生錯誤!’);  
  33.         END GET_EMPLOYE;  
  34. ———————- 保存僱員到EMP表 ————————–  
  35.         PROCEDURE SAVE_EMPLOYE  
  36.         AS  
  37.         BEGIN  
  38.         UPDATE EMP SET ENAME=EMPLOYE.ENAME, SAL=EMPLOYE.SAL WHERE EMPNO=  
  39.     EMPLOYE.EMPNO;  
  40.      DBMS_OUTPUT.PUT_LINE(’僱員信息保存完成!’);  
  41.         END SAVE_EMPLOYE;  
  42. —————————- 修改僱員名稱 ——————————  
  43.         PROCEDURE CHANGE_NAME(P_NEWNAME VARCHAR2)  
  44.          AS  
  45.         BEGIN  
  46.          EMPLOYE.ENAME:=P_NEWNAME;  
  47.          DBMS_OUTPUT.PUT_LINE(’修改名稱完成!’);  
  48.         END CHANGE_NAME;  
  49. —————————- 修改僱員工資 ————————–  
  50.         PROCEDURE CHANGE_SAL(P_NEWSAL NUMBER)  
  51.         AS  
  52.         BEGIN  
  53.          EMPLOYE.SAL:=P_NEWSAL;  
  54.          DBMS_OUTPUT.PUT_LINE(’修改工資完成!’);  
  55.         END CHANGE_SAL;  
  56.         END EMPLOYE;  



步驟2:獲取僱員7788的信息:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2.         EXECUTE EMPLOYE.GET_EMPLOYE(7788);  

  1. SET SERVEROUTPUT ON  
  2.         EXECUTE EMPLOYE.GET_EMPLOYE(7788);  



結果爲:
Sql代碼 複製代碼
  1. 獲取僱員SCOTT信息成功   
  2.         PL/SQL 過程已成功完成。  

  1. 獲取僱員SCOTT信息成功  
  2.         PL/SQL 過程已成功完成。  



步驟3:顯示僱員信息:
Sql代碼 複製代碼
  1. EXECUTE EMPLOYE.SHOW_DETAIL;  

  1. EXECUTE EMPLOYE.SHOW_DETAIL;  



結果爲:
Sql代碼 複製代碼
  1. —————— 僱員信息 ——————  
  2.         僱員編號:7788   
  3.         僱員名稱:SCOTT   
  4.         僱員職務:ANALYST   
  5.         僱員工資:3000   
  6.         部門編號:20   
  7.         PL/SQL 過程已成功完成。  

  1. —————— 僱員信息 ——————  
  2.         僱員編號:7788  
  3.         僱員名稱:SCOTT  
  4.         僱員職務:ANALYST  
  5.         僱員工資:3000  
  6.         部門編號:20  
  7.         PL/SQL 過程已成功完成。  



步驟4:修改僱員工資:
Sql代碼 複製代碼
  1. EXECUTE EMPLOYE.CHANGE_SAL(3800);  

  1. EXECUTE EMPLOYE.CHANGE_SAL(3800);  



結果爲:
Sql代碼 複製代碼
  1. 修改工資完成!   
  2.         PL/SQL 過程已成功完成。  

  1. 修改工資完成!  
  2.         PL/SQL 過程已成功完成。  



步驟5:將修改的僱員信息存入EMP表
Sql代碼 複製代碼
  1. EXECUTE EMPLOYE.SAVE_EMPLOYE;  

  1. EXECUTE EMPLOYE.SAVE_EMPLOYE;  



結果爲:
Sql代碼 複製代碼
  1. 僱員信息保存完成!   
  2.         PL/SQL 過程已成功完成。  

  1. 僱員信息保存完成!  
  2.         PL/SQL 過程已成功完成。  



說明:該包完成將EMP表中的某個僱員的信息取入內存記錄變量,在記錄變量中進行修改編輯,在確認顯示信息正確後寫回EMP表的功能。記錄變量EMPLOYE用來存儲取得的僱員信息,定義爲私有變量,只能被包的內部模塊訪問。
  【練習1】爲包增加修改僱員職務和部門編號的功能。

階段訓練
下面的訓練通過定義和創建完整的包EMP_PK並綜合運用本章的知識,完成對僱員表的插入、刪除等功能,包中的主要元素解釋如下所示。
Sql代碼 複製代碼
  1. 程序結構    類  型    說    明   
  2. V_EMP_COUNT 公有變量    跟蹤僱員的總人數變化,插入、刪除僱員的同時修改該變量的值   
  3. INIT    公有過程    對包進行初始化,初始化僱員人數和工資修改的上、下限   
  4. LIST_EMP    公有過程    顯示僱員列表   
  5. INSERT_EMP  公有過程    通過編號插入新僱員   
  6. DELETE_EMP  公有過程    通過編號刪除僱員   
  7. CHANGE_EMP_SAL  公有過程    通過編號修改僱員工資   
  8. V_MESSAGE   私有變量    存放準備輸出的信息   
  9. C_MAX_SAL   私有變量    對工資修改的上限   
  10. C_MIN_SAL   私有變量    對工資修改的下限   
  11. SHOW_MESSAGE    私有過程    顯示私有變量V_MESSAGE中的信息   
  12. EXIST_EMP   私有函數    判斷某個編號的僱員是否存在,該函數被INSERT_EMP、DELETE_EMP和CHANGE_EMP_SAL等過程調用  

  1. 程序結構    類  型    說    明  
  2. V_EMP_COUNT 公有變量    跟蹤僱員的總人數變化,插入、刪除僱員的同時修改該變量的值  
  3. INIT    公有過程    對包進行初始化,初始化僱員人數和工資修改的上、下限  
  4. LIST_EMP    公有過程    顯示僱員列表  
  5. INSERT_EMP  公有過程    通過編號插入新僱員  
  6. DELETE_EMP  公有過程    通過編號刪除僱員  
  7. CHANGE_EMP_SAL  公有過程    通過編號修改僱員工資  
  8. V_MESSAGE   私有變量    存放準備輸出的信息  
  9. C_MAX_SAL   私有變量    對工資修改的上限  
  10. C_MIN_SAL   私有變量    對工資修改的下限  
  11. SHOW_MESSAGE    私有過程    顯示私有變量V_MESSAGE中的信息  
  12. EXIST_EMP   私有函數    判斷某個編號的僱員是否存在,該函數被INSERT_EMP、DELETE_EMP和CHANGE_EMP_SAL等過程調用  



【訓練1】  完整的僱員包EMP_PK的創建和應用。
步驟1:在SQL*Plus中登錄SCOTT賬戶,輸入以下包頭和包體部分,按“執行”按鈕編譯:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PACKAGE EMP_PK    
  2.         –包頭部分   
  3.         IS  
  4.         V_EMP_COUNT NUMBER(5);                 
  5.         –僱員人數  
  6.         PROCEDURE INIT(P_MAX NUMBER,P_MIN NUMBER);  –初始化  
  7.         PROCEDURE LIST_EMP;                        
  8.         –顯示僱員列表  
  9. PROCEDURE INSERT_EMP(P_EMPNO        NUMBER,P_ENAMEVARCHAR2,P_JOB VARCHAR2,   
  10.         P_SAL NUMBER);                         
  11.         –插入僱員  
  12.         PROCEDURE DELETE_EMP(P_EMPNO NUMBER);       –刪除僱員  
  13.          PROCEDURE CHANGE_EMP_SAL(P_EMPNO NUMBER,P_SAL NUMBER);    
  14.         –修改僱員工資  
  15.         END EMP_PK;   
  16.         /CREATE OR REPLACE PACKAGE BODY EMP_PK   
  17.          –包體部分   
  18.         IS  
  19.         V_MESSAGE VARCHAR2(50); –顯示信息  
  20. V_MAX_SAL NUMBER(7); –工資上限  
  21.         V_MIN_SAL NUMBER(7); –工資下限  
  22.         FUNCTION EXIST_EMP(P_EMPNO NUMBER)  RETURN  BOOLEAN; –判斷僱員是否存在函數  
  23.         PROCEDURE SHOW_MESSAGE; –顯示信息過程  
  24.         ——————————- 初始化過程 —————————-  
  25.         PROCEDURE INIT(P_MAX NUMBER,P_MIN NUMBER)    
  26.         IS    
  27.         BEGIN  
  28.          SELECT COUNT(*) INTO V_EMP_COUNT FROM EMP;   
  29. V_MAX_SAL:=P_MAX;   
  30.          V_MIN_SAL:=P_MIN;   
  31.          V_MESSAGE:=’初始化過程已經完成!’;   
  32.          SHOW_MESSAGE;    
  33.         END INIT;   
  34. —————————- 顯示僱員列表過程 ———————  
  35.         PROCEDURE LIST_EMP    
  36.          IS    
  37.         BEGIN  
  38. DBMS_OUTPUT.PUT_LINE(’姓名       職務      工資’);   
  39.         FOR emp_rec IN (SELECT * FROM EMP)   
  40.         LOOP   
  41.     DBMS_OUTPUT.PUT_LINE(RPAD(emp_rec.ename,10,)||RPAD(emp_rec.job,10,’ ’)||TO_CHAR(emp_rec.sal));   
  42.          END LOOP;   
  43.          DBMS_OUTPUT.PUT_LINE(’僱員總人數’||V_EMP_COUNT);   
  44.         END LIST_EMP;   
  45. —————————– 插入僱員過程 —————————–  
  46.         PROCEDUREINSERT_EMP(P_EMPNO     NUMBER,P_ENAMEVARCHAR2,P_JOB    VARCHAR2,P_SAL NUMBER)   
  47.          IS    
  48.         BEGIN  
  49.         IF NOT EXIST_EMP(P_EMPNO) THEN  
  50.         INSERT INTO EMP(EMPNO,ENAME,JOB,SAL)        VALUES(P_EMPNO,P_ENAME,P_JOB,P_SAL);   
  51.         COMMIT;    
  52.         V_EMP_COUNT:=V_EMP_COUNT+1;   
  53.         V_MESSAGE:=’僱員’||P_EMPNO||’已插入!’;   
  54.         ELSE  
  55. V_MESSAGE:=’僱員’||P_EMPNO||’已存在,不能插入!’;   
  56.       END IF;   
  57.      SHOW_MESSAGE;    
  58.      EXCEPTION   
  59.     WHEN OTHERS THEN  
  60.      V_MESSAGE:=’僱員’||P_EMPNO||’插入失敗!’;   
  61.      SHOW_MESSAGE;   
  62.      END INSERT_EMP;   
  63. ————————— 刪除僱員過程 ——————–  
  64.          PROCEDURE DELETE_EMP(P_EMPNO NUMBER)    
  65.         IS    
  66.         BEGIN    
  67.         IF EXIST_EMP(P_EMPNO) THEN  
  68.         DELETE FROM EMP WHERE EMPNO=P_EMPNO;   
  69.         COMMIT;   
  70.          V_EMP_COUNT:=V_EMP_COUNT-1;   
  71.          V_MESSAGE:=’僱員’||P_EMPNO||’已刪除!’;   
  72.          ELSE  
  73. V_MESSAGE:=’僱員’||P_EMPNO||’不存在,不能刪除!’;   
  74.     END IF;   
  75.     SHOW_MESSAGE;   
  76.      EXCEPTION   
  77.      WHEN OTHERS THEN  
  78.      V_MESSAGE:=’僱員’||P_EMPNO||’刪除失敗!’;   
  79.      SHOW_MESSAGE;   
  80.     END DELETE_EMP;   
  81. ————————————— 修改僱員工資過程 ————————————  
  82.         PROCEDURE CHANGE_EMP_SAL(P_EMPNO NUMBER,P_SAL NUMBER)    
  83.          IS    
  84.          BEGIN    
  85.          IF (P_SAL>V_MAX_SAL OR P_SAL<V_MIN_SAL) THEN  
  86.          V_MESSAGE:=’工資超出修改範圍!’;   
  87.         ELSIF NOT EXIST_EMP(P_EMPNO) THEN  
  88.         V_MESSAGE:=’僱員’||P_EMPNO||’不存在,不能修改工資!’;   
  89. ELSE  
  90.          UPDATE EMP SET SAL=P_SAL WHERE EMPNO=P_EMPNO;   
  91.         COMMIT;   
  92.         V_MESSAGE:=’僱員’||P_EMPNO||’工資已經修改!’;   
  93.         END IF;   
  94.         SHOW_MESSAGE;   
  95.         EXCEPTION   
  96.          WHEN OTHERS THEN  
  97.          V_MESSAGE:=’僱員’||P_EMPNO||’工資修改失敗!’;   
  98.          SHOW_MESSAGE;   
  99.          END CHANGE_EMP_SAL;   
  100. —————————- 顯示信息過程 —————————-  
  101.          PROCEDURE SHOW_MESSAGE    
  102.         IS    
  103.         BEGIN  
  104.          DBMS_OUTPUT.PUT_LINE(’提示信息:’||V_MESSAGE);   
  105.         END SHOW_MESSAGE;   
  106. ———————— 判斷僱員是否存在函數 ——————-  
  107.          FUNCTION EXIST_EMP(P_EMPNO NUMBER)   
  108.          RETURN BOOLEAN    
  109.          IS  
  110.         V_NUM NUMBER; –局部變量  
  111.         BEGIN  
  112.         SELECT COUNT(*) INTO V_NUM FROM EMP WHERE EMPNO=P_EMPNO;   
  113. IF V_NUM=1 THEN    
  114.            RETURN TRUE;   
  115.          ELSE  
  116.          RETURN FALSE;   
  117.         END IF;    
  118.         END EXIST_EMP;   
  119.         —————————–  
  120.         END EMP_PK;  

  1. CREATE OR REPLACE PACKAGE EMP_PK   
  2.         –包頭部分   
  3.         IS  
  4.         V_EMP_COUNT NUMBER(5);                
  5.         –僱員人數  
  6.         PROCEDURE INIT(P_MAX NUMBER,P_MIN NUMBER);  –初始化  
  7.         PROCEDURE LIST_EMP;                       
  8.         –顯示僱員列表  
  9. PROCEDURE INSERT_EMP(P_EMPNO        NUMBER,P_ENAMEVARCHAR2,P_JOB VARCHAR2,  
  10.         P_SAL NUMBER);                        
  11.         –插入僱員  
  12.         PROCEDURE DELETE_EMP(P_EMPNO NUMBER);       –刪除僱員  
  13.          PROCEDURE CHANGE_EMP_SAL(P_EMPNO NUMBER,P_SAL NUMBER);   
  14.         –修改僱員工資  
  15.         END EMP_PK;  
  16.         /CREATE OR REPLACE PACKAGE BODY EMP_PK  
  17.          –包體部分   
  18.         IS  
  19.         V_MESSAGE VARCHAR2(50); –顯示信息  
  20. V_MAX_SAL NUMBER(7); –工資上限  
  21.         V_MIN_SAL NUMBER(7); –工資下限  
  22.         FUNCTION EXIST_EMP(P_EMPNO NUMBER)  RETURN  BOOLEAN; –判斷僱員是否存在函數  
  23.         PROCEDURE SHOW_MESSAGE; –顯示信息過程  
  24.         ——————————- 初始化過程 —————————-  
  25.         PROCEDURE INIT(P_MAX NUMBER,P_MIN NUMBER)   
  26.         IS   
  27.         BEGIN  
  28.          SELECT COUNT(*) INTO V_EMP_COUNT FROM EMP;  
  29. V_MAX_SAL:=P_MAX;  
  30.          V_MIN_SAL:=P_MIN;  
  31.          V_MESSAGE:=’初始化過程已經完成!’;  
  32.          SHOW_MESSAGE;   
  33.         END INIT;  
  34. —————————- 顯示僱員列表過程 ———————  
  35.         PROCEDURE LIST_EMP   
  36.          IS   
  37.         BEGIN  
  38. DBMS_OUTPUT.PUT_LINE(’姓名       職務      工資’);  
  39.         FOR emp_rec IN (SELECT * FROM EMP)  
  40.         LOOP  
  41.     DBMS_OUTPUT.PUT_LINE(RPAD(emp_rec.ename,10,)||RPAD(emp_rec.job,10,‘ ’)||TO_CHAR(emp_rec.sal));  
  42.          END LOOP;  
  43.          DBMS_OUTPUT.PUT_LINE(’僱員總人數’||V_EMP_COUNT);  
  44.         END LIST_EMP;  
  45. —————————– 插入僱員過程 —————————–  
  46.         PROCEDUREINSERT_EMP(P_EMPNO     NUMBER,P_ENAMEVARCHAR2,P_JOB    VARCHAR2,P_SAL NUMBER)  
  47.          IS   
  48.         BEGIN  
  49.         IF NOT EXIST_EMP(P_EMPNO) THEN  
  50.         INSERT INTO EMP(EMPNO,ENAME,JOB,SAL)        VALUES(P_EMPNO,P_ENAME,P_JOB,P_SAL);  
  51.         COMMIT;   
  52.         V_EMP_COUNT:=V_EMP_COUNT+1;  
  53.         V_MESSAGE:=’僱員’||P_EMPNO||‘已插入!’;  
  54.         ELSE  
  55. V_MESSAGE:=’僱員’||P_EMPNO||‘已存在,不能插入!’;  
  56.       END IF;  
  57.      SHOW_MESSAGE;   
  58.      EXCEPTION  
  59.     WHEN OTHERS THEN  
  60.      V_MESSAGE:=’僱員’||P_EMPNO||‘插入失敗!’;  
  61.      SHOW_MESSAGE;  
  62.      END INSERT_EMP;  
  63. ————————— 刪除僱員過程 ——————–  
  64.          PROCEDURE DELETE_EMP(P_EMPNO NUMBER)   
  65.         IS   
  66.         BEGIN   
  67.         IF EXIST_EMP(P_EMPNO) THEN  
  68.         DELETE FROM EMP WHERE EMPNO=P_EMPNO;  
  69.         COMMIT;  
  70.          V_EMP_COUNT:=V_EMP_COUNT-1;  
  71.          V_MESSAGE:=’僱員’||P_EMPNO||‘已刪除!’;  
  72.          ELSE  
  73. V_MESSAGE:=’僱員’||P_EMPNO||‘不存在,不能刪除!’;  
  74.     END IF;  
  75.     SHOW_MESSAGE;  
  76.      EXCEPTION  
  77.      WHEN OTHERS THEN  
  78.      V_MESSAGE:=’僱員’||P_EMPNO||‘刪除失敗!’;  
  79.      SHOW_MESSAGE;  
  80.     END DELETE_EMP;  
  81. ————————————— 修改僱員工資過程 ————————————  
  82.         PROCEDURE CHANGE_EMP_SAL(P_EMPNO NUMBER,P_SAL NUMBER)   
  83.          IS   
  84.          BEGIN   
  85.          IF (P_SAL>V_MAX_SAL OR P_SAL<V_MIN_SAL) THEN  
  86.          V_MESSAGE:=’工資超出修改範圍!’;  
  87.         ELSIF NOT EXIST_EMP(P_EMPNO) THEN  
  88.         V_MESSAGE:=’僱員’||P_EMPNO||‘不存在,不能修改工資!’;  
  89. ELSE  
  90.          UPDATE EMP SET SAL=P_SAL WHERE EMPNO=P_EMPNO;  
  91.         COMMIT;  
  92.         V_MESSAGE:=’僱員’||P_EMPNO||‘工資已經修改!’;  
  93.         END IF;  
  94.         SHOW_MESSAGE;  
  95.         EXCEPTION  
  96.          WHEN OTHERS THEN  
  97.          V_MESSAGE:=’僱員’||P_EMPNO||‘工資修改失敗!’;  
  98.          SHOW_MESSAGE;  
  99.          END CHANGE_EMP_SAL;  
  100. —————————- 顯示信息過程 —————————-  
  101.          PROCEDURE SHOW_MESSAGE   
  102.         IS   
  103.         BEGIN  
  104.          DBMS_OUTPUT.PUT_LINE(’提示信息:’||V_MESSAGE);  
  105.         END SHOW_MESSAGE;  
  106. ———————— 判斷僱員是否存在函數 ——————-  
  107.          FUNCTION EXIST_EMP(P_EMPNO NUMBER)  
  108.          RETURN BOOLEAN   
  109.          IS  
  110.         V_NUM NUMBER; –局部變量  
  111.         BEGIN  
  112.         SELECT COUNT(*) INTO V_NUM FROM EMP WHERE EMPNO=P_EMPNO;  
  113. IF V_NUM=1 THEN   
  114.            RETURN TRUE;  
  115.          ELSE  
  116.          RETURN FALSE;  
  117.         END IF;   
  118.         END EXIST_EMP;  
  119.         —————————–  
  120.         END EMP_PK;  


結果爲:
Sql代碼 複製代碼
  1. 程序包已創建。   
  2.         程序包主體已創建。  

  1. 程序包已創建。  
  2.         程序包主體已創建。  



步驟2:初始化包:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2. EXECUTE EMP_PK.INIT(6000,600);  

  1. SET SERVEROUTPUT ON  
  2. EXECUTE EMP_PK.INIT(6000,600);  



顯示爲:
Sql代碼 複製代碼
  1. 提示信息:初始化過程已經完成!  

  1. 提示信息:初始化過程已經完成!  



步驟3:顯示僱員列表:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.LIST_EMP;  

  1. EXECUTE EMP_PK.LIST_EMP;  



顯示爲:
Sql代碼 複製代碼
  1. 姓名          職務          工資   
  2.         SMITH       CLERK       1560   
  3.         ALLEN       SALESMAN    1936   
  4.         WARD        SALESMAN    1830   
  5.         JONES       MANAGER     2975   
  6.         …   
  7.         僱員總人數:14   
  8.         PL/SQL 過程已成功完成。  

  1. 姓名          職務          工資  
  2.         SMITH       CLERK       1560  
  3.         ALLEN       SALESMAN    1936  
  4.         WARD        SALESMAN    1830  
  5.         JONES       MANAGER     2975  
  6.         …  
  7.         僱員總人數:14  
  8.         PL/SQL 過程已成功完成。  



步驟4:插入一個新記錄:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.INSERT_EMP(8001,’小王’,’CLERK’,1000);  

  1. EXECUTE EMP_PK.INSERT_EMP(8001,‘小王’,‘CLERK’,1000);  



顯示結果爲:
Sql代碼 複製代碼
  1. 提示信息:僱員8001已插入!   
  2. PL/SQL 過程已成功完成。  

  1. 提示信息:僱員8001已插入!  
  2. PL/SQL 過程已成功完成。  



步驟5:通過全局變量V_EMP_COUNT查看僱員人數:
Sql代碼 複製代碼
  1. BEGIN  
  2. DBMS_OUTPUT.PUT_LINE(EMP_PK.V_EMP_COUNT);   
  3. END;  

  1. BEGIN  
  2. DBMS_OUTPUT.PUT_LINE(EMP_PK.V_EMP_COUNT);  
  3. END;  



顯示結果爲:
Sql代碼 複製代碼
  1. 15   
  2. PL/SQL 過程已成功完成。  

  1. 15  
  2. PL/SQL 過程已成功完成。  



步驟6:刪除新插入記錄:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.DELETE_EMP(8001);  

  1. EXECUTE EMP_PK.DELETE_EMP(8001);  



顯示結果爲:
Sql代碼 複製代碼
  1. 提示信息:僱員8001已刪除!   
  2.         PL/SQL 過程已成功完成。  

  1. 提示信息:僱員8001已刪除!  
  2.         PL/SQL 過程已成功完成。  



再次刪除該僱員:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.DELETE_EMP(8001);  

  1. EXECUTE EMP_PK.DELETE_EMP(8001);  



結果爲:
Sql代碼 複製代碼
  1. 提示信息:僱員8001不存在,不能刪除!  

  1. 提示信息:僱員8001不存在,不能刪除!  



步驟7:修改僱員工資:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.CHANGE_EMP_SAL(7788,8000);  

  1. EXECUTE EMP_PK.CHANGE_EMP_SAL(7788,8000);  



顯示結果爲:
Sql代碼 複製代碼
  1. 提示信息:工資超出修改範圍!   
  2.         PL/SQL 過程已成功完成。  

  1. 提示信息:工資超出修改範圍!  
  2.         PL/SQL 過程已成功完成。  



步驟8:授權其他用戶調用包:
如果是另外一個用戶要使用該包,必須由包的所有者授權,下面授予STUDEN賬戶對該包的使用權:
Sql代碼 複製代碼
  1. GRANT EXECUTE ON EMP_PK TO STUDENT;  

  1. GRANT EXECUTE ON EMP_PK TO STUDENT;  



每一個新的會話要爲包中的公用變量開闢新的存儲空間,所以需要重新執行初始化過程。兩個會話的進程互不影響。
步驟9:其他用戶調用包。
啓動另外一個SQL*Plus,登錄STUDENT賬戶,執行以下過程:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2.         EXECUTE SCOTT.EMP_PK. EMP_PK.INIT(5000,700);  

  1. SET SERVEROUTPUT ON  
  2.         EXECUTE SCOTT.EMP_PK. EMP_PK.INIT(5000,700);  



結果爲:
Sql代碼 複製代碼
  1. 提示信息:初始化過程已經完成!   
  2.         PL/SQL 過程已成功完成。  

  1. 提示信息:初始化過程已經完成!  
  2.         PL/SQL 過程已成功完成。  



說明:在初始化中設置僱員的總人數和修改工資的上、下限,初始化後V_EMP_COUNT爲14人,插入僱員後V_EMP_COUNT爲15人。V_EMP_COUNT爲公有變量,所以可以在外部程序中使用DBMS_OUTPUT.PUT_LINE輸出,引用時用EMP_PK.V_EMP_COUNT的形式,說明所屬的包。而私有變量V_MAX_SAL和V_MIN_SAL不能被外部訪問,只能通過內部過程來修改。同樣,EXIST_EMP和SHOW_MESSAGE也是私有過程,也只能在過程體內被其他模塊引用。
注意:在最後一個步驟中,因爲STUDENT模式調用了SCOTT模式的包,所以包名前要增加模式名SCOTT。不同的會話對包的調用屬於不同的應用,所以需要重新進行初始化。
練習
1.如果存儲過程的參數類型爲OUT,那麼調用時傳遞的參數應該爲:
     A.常量 B.表達式                C.變量 D.都可以
2.下列有關存儲過程的特點說法錯誤的是:
     A.存儲過程不能將值傳回調用的主程序
     B.存儲過程是一個命名的模塊
     C.編譯的存儲過程存放在數據庫中
     D.一個存儲過程可以調用另一個存儲過程
3.下列有關函數的特點說法錯誤的是:
     A.函數必須定義返回類型
     B.函數參數的類型只能是IN
     C.在函數體內可以多次使用RETURN語句
     D.函數的調用應使用EXECUTE命令
4.包中不能包含的元素爲:
     A.存儲過程 B.存儲函數
     C.遊標    D.表
5.下列有關包的使用說法錯誤的是:
     A.在不同的包內模塊可以重名
     B.包的私有過程不能被外部程序調用
     C.包體中的過程和函數必須在包頭部分說明
     D.必須先創建包頭,然後創建包體

oracle存儲過程實例

分類: 數據(倉)庫及處理 1055人閱讀 評論(2)收藏舉報
認識存儲過程和函數
存儲過程和函數也是一種PL/SQL塊,是存入數據庫的PL/SQL塊。但存儲過程和函數不同於已經介紹過的PL/SQL程序,我們通常把PL/SQL程序稱爲無名塊,而存儲過程和函數是以命名的方式存儲於數據庫中的。和PL/SQL程序相比,存儲過程有很多優點,具體歸納如下:
* 存儲過程和函數以命名的數據庫對象形式存儲於數據庫當中。存儲在數據庫中的優點是很明顯的,因爲代碼不保存在本地,用戶可以在任何客戶機上登錄到數據庫,並調用或修改代碼。
* 存儲過程和函數可由數據庫提供安全保證,要想使用存儲過程和函數,需要有存儲過程和函數的所有者的授權,只有被授權的用戶或創建者本身才能執行存儲過程或調用函數。
* 存儲過程和函數的信息是寫入數據字典的,所以存儲過程可以看作是一個公用模塊,用戶編寫的PL/SQL程序或其他存儲過程都可以調用它(但存儲過程和函數不能調用PL/SQL程序)。一個重複使用的功能,可以設計成爲存儲過程,比如:顯示一張工資統計表,可以設計成爲存儲過程;一個經常調用的計算,可以設計成爲存儲函數;根據僱員編號返回僱員的姓名,可以設計成存儲函數。
* 像其他高級語言的過程和函數一樣,可以傳遞參數給存儲過程或函數,參數的傳遞也有多種方式。存儲過程可以有返回值,也可以沒有返回值,存儲過程的返回值必須通過參數帶回;函數有一定的數據類型,像其他的標準函數一樣,我們可以通過對函數名的調用返回函數值。
   存儲過程和函數需要進行編譯,以排除語法錯誤,只有編譯通過才能調用。
創建和刪除存儲過程
創建存儲過程,需要有CREATE PROCEDURE或CREATE ANY PROCEDURE的系統權限。該權限可由系統管理員授予。創建一個存儲過程的基本語句如下:
CREATE [OR REPLACE] PROCEDURE 存儲過程名[(參數[IN|OUT|IN OUT] 數據類型…)]
{AS|IS}
[說明部分]
BEGIN
可執行部分
[EXCEPTION
錯誤處理部分]
END [過程名];
其中:
可選關鍵字OR REPLACE 表示如果存儲過程已經存在,則用新的存儲過程覆蓋,通常用於存儲過程的重建。
參數部分用於定義多個參數(如果沒有參數,就可以省略)。參數有三種形式:IN、OUT和IN OUT。如果沒有指明參數的形式,則默認爲IN。
關鍵字AS也可以寫成IS,後跟過程的說明部分,可以在此定義過程的局部變量。
編寫存儲過程可以使用任何文本編輯器或直接在SQL*Plus環境下進行,編寫好的存儲過程必須要在SQL*Plus環境下進行編譯,生成編譯代碼,原代碼和編譯代碼在編譯過程中都會被存入數據庫。編譯成功的存儲過程就可以在Oracle環境下進行調用了。
一個存儲過程在不需要時可以刪除。刪除存儲過程的人是過程的創建者或者擁有DROP ANY PROCEDURE系統權限的人。刪除存儲過程的語法如下:
DROP PROCEDURE 存儲過程名;
如果要重新編譯一個存儲過程,則只能是過程的創建者或者擁有ALTER ANY PROCEDURE系統權限的人。語法如下:
ALTER PROCEDURE 存儲過程名 COMPILE;
執行(或調用)存儲過程的人是過程的創建者或是擁有EXECUTE ANY PROCEDURE系統權限的人或是被擁有者授予EXECUTE權限的人。執行的方法如下:
方法1:
EXECUTE 模式名.存儲過程名[(參數…)];
方法2:
BEGIN
模式名.存儲過程名[(參數…)];
END;
傳遞的參數必須與定義的參數類型、個數和順序一致(如果參數定義了默認值,則調用時可以省略參數)。參數可以是變量、常量或表達式,用法參見下一節。
如果是調用本賬戶下的存儲過程,則模式名可以省略。要調用其他賬戶編寫的存儲過程,則模式名必須要添加。
以下是一個生成和調用簡單存儲過程的訓練。注意要事先授予創建存儲過程的權限。
【訓練1】  創建一個顯示僱員總人數的存儲過程。
步驟1:登錄SCOTT賬戶(或學生個人賬戶)。
步驟2:在SQL*Plus輸入區中,輸入以下存儲過程:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE EMP_COUNT   
  2. AS  
  3. V_TOTAL NUMBER(10);   
  4. BEGIN  
  5.  SELECT COUNT(*) INTO V_TOTAL FROM EMP;   
  6.  DBMS_OUTPUT.PUT_LINE(’僱員總人數爲:’||V_TOTAL);   
  7. END;  

  1. CREATE OR REPLACE PROCEDURE EMP_COUNT  
  2. AS  
  3. V_TOTAL NUMBER(10);  
  4. BEGIN  
  5.  SELECT COUNT(*) INTO V_TOTAL FROM EMP;  
  6.  DBMS_OUTPUT.PUT_LINE(’僱員總人數爲:’||V_TOTAL);  
  7. END;  



步驟3:按“執行”按鈕進行編譯。
如果存在錯誤,就會顯示:
警告: 創建的過程帶有編譯錯誤。
如果存在錯誤,對腳本進行修改,直到沒有錯誤產生。
如果編譯結果正確,將顯示:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟4:調用存儲過程,在輸入區中輸入以下語句並執行:
Sql代碼 複製代碼
  1. EXECUTE EMP_COUNT;  

  1. EXECUTE EMP_COUNT;  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員總人數爲:14   
  2.         PL/SQL 過程已成功完成。  

  1. 僱員總人數爲:14  
  2.         PL/SQL 過程已成功完成。  



說明:在該訓練中,V_TOTAL變量是存儲過程定義的局部變量,用於接收查詢到的僱員總人數。
注意:在SQL*Plus中輸入存儲過程,按“執行”按鈕是進行編譯,不是執行存儲過程。
  如果在存儲過程中引用了其他用戶的對象,比如表,則必須有其他用戶授予的對象訪問權限。一個存儲過程一旦編譯成功,就可以由其他用戶或程序來引用。但存儲過程或函數的所有者必須授予其他用戶執行該過程的權限。
存儲過程沒有參數,在調用時,直接寫過程名即可。
【訓練2】  在PL/SQL程序中調用存儲過程。
步驟1:登錄SCOTT賬戶。
步驟2:授權STUDENT賬戶使用該存儲過程,即在SQL*Plus輸入區中,輸入以下的命令:
Sql代碼 複製代碼
  1. GRANT EXECUTE ON EMP_COUNT TO STUDENT  

  1. GRANT EXECUTE ON EMP_COUNT TO STUDENT  



Sql代碼 複製代碼
  1. 授權成功。  

  1. 授權成功。  



步驟3:登錄STUDENT賬戶,在SQL*Plus輸入區中輸入以下程序:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2.         BEGIN  
  3.         SCOTT.EMP_COUNT;   
  4.         END;  

  1. SET SERVEROUTPUT ON  
  2.         BEGIN  
  3.         SCOTT.EMP_COUNT;  
  4.         END;  



步驟4:執行以上程序,結果爲:
Sql代碼 複製代碼
  1. 僱員總人數爲:14   
  2.         PL/SQL 過程已成功完成。   

  1. 僱員總人數爲:14  
  2.         PL/SQL 過程已成功完成。   



  說明:在本例中,存儲過程是由SCOTT賬戶創建的,STUDEN賬戶獲得SCOTT賬戶的授權後,才能調用該存儲過程。
  注意:在程序中調用存儲過程,使用了第二種語法。
【訓練3】  編寫顯示僱員信息的存儲過程EMP_LIST,並引用EMP_COUNT存儲過程。
步驟1:在SQL*Plus輸入區中輸入並編譯以下存儲過程:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE EMP_LIST   
  2.         AS  
  3.          CURSOR emp_cursor IS    
  4.         SELECT empno,ename FROM emp;   
  5.         BEGIN  
  6. FOR Emp_record IN emp_cursor LOOP      
  7.     DBMS_OUTPUT.PUT_LINE(Emp_record.empno||Emp_record.ename);   
  8.         END LOOP;   
  9.         EMP_COUNT;   
  10.         END;  

  1. CREATE OR REPLACE PROCEDURE EMP_LIST  
  2.         AS  
  3.          CURSOR emp_cursor IS   
  4.         SELECT empno,ename FROM emp;  
  5.         BEGIN  
  6. FOR Emp_record IN emp_cursor LOOP     
  7.     DBMS_OUTPUT.PUT_LINE(Emp_record.empno||Emp_record.ename);  
  8.         END LOOP;  
  9.         EMP_COUNT;  
  10.         END;  



執行結果:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟2:調用存儲過程,在輸入區中輸入以下語句並執行:
Sql代碼 複製代碼
  1. EXECUTE EMP_LIST  

  1. EXECUTE EMP_LIST  



顯示結果爲:
Sql代碼 複製代碼
  1. 7369SMITH   
  2. 7499ALLEN   
  3. 7521WARD   
  4. 7566JONES   
  5.             執行結果:   
  6.         僱員總人數爲:14   
  7.         PL/SQL 過程已成功完成。  

  1. 7369SMITH  
  2. 7499ALLEN  
  3. 7521WARD  
  4. 7566JONES  
  5.             執行結果:  
  6.         僱員總人數爲:14  
  7.         PL/SQL 過程已成功完成。  



說明:以上的EMP_LIST存儲過程中定義並使用了遊標,用來循環顯示所有僱員的信息。然後調用已經成功編譯的存儲過程EMP_COUNT,用來附加顯示僱員總人數。通過EXECUTE命令來執行EMP_LIST存儲過程。
【練習1】編寫顯示部門信息的存儲過程DEPT_LIST,要求統計出部門個數。
參數傳遞
參數的作用是向存儲過程傳遞數據,或從存儲過程獲得返回結果。正確的使用參數可以大大增加存儲過程的靈活性和通用性。
參數的類型有三種,如下所示。
Sql代碼 複製代碼
  1. IN  定義一個輸入參數變量,用於傳遞參數給存儲過程   
  2. OUT 定義一個輸出參數變量,用於從存儲過程獲取數據   
  3. IN OUT  定義一個輸入、輸出參數變量,兼有以上兩者的功能  

  1. IN  定義一個輸入參數變量,用於傳遞參數給存儲過程  
  2. OUT 定義一個輸出參數變量,用於從存儲過程獲取數據  
  3. IN OUT  定義一個輸入、輸出參數變量,兼有以上兩者的功能  



參數的定義形式和作用如下:
參數名 IN 數據類型 DEFAULT 值;
定義一個輸入參數變量,用於傳遞參數給存儲過程。在調用存儲過程時,主程序的實際參數可以是常量、有值變量或表達式等。DEFAULT 關鍵字爲可選項,用來設定參數的默認值。如果在調用存儲過程時不指明參數,則參數變量取默認值。在存儲過程中,輸入變量接收主程序傳遞的值,但不能對其進行賦值。
參數名 OUT 數據類型;
定義一個輸出參數變量,用於從存儲過程獲取數據,即變量從存儲過程中返回值給主程序。
在調用存儲過程時,主程序的實際參數只能是一個變量,而不能是常量或表達式。在存儲過程中,參數變量只能被賦值而不能將其用於賦值,在存儲過程中必須給輸出變量至少賦值一次。
參數名 IN OUT 數據類型 DEFAULT 值;
定義一個輸入、輸出參數變量,兼有以上兩者的功能。在調用存儲過程時,主程序的實際參數只能是一個變量,而不能是常量或表達式。DEFAULT 關鍵字爲可選項,用來設定參數的默認值。在存儲過程中,變量接收主程序傳遞的值,同時可以參加賦值運算,也可以對其進行賦值。在存儲過程中必須給變量至少賦值一次。
如果省略IN、OUT或IN OUT,則默認模式是IN。
【訓練1】  編寫給僱員增加工資的存儲過程CHANGE_SALARY,通過IN類型的參數傳遞要增加工資的僱員編號和增加的工資額。
步驟1:登錄SCOTT賬戶。
  步驟2:在SQL*Plus輸入區中輸入以下存儲過程並執行:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE CHANGE_SALARY(P_EMPNO IN NUMBER DEFAULT 7788,P_RAISE NUMBER DEFAULT 10)   
  2.         AS  
  3.          V_ENAME VARCHAR2(10);   
  4. V_SAL NUMBER(5);   
  5.         BEGIN  
  6.         SELECT ENAME,SAL INTO V_ENAME,V_SAL FROM EMP WHERE EMPNO=P_EMPNO;   
  7.          UPDATE EMP SET SAL=SAL+P_RAISE WHERE EMPNO=P_EMPNO;   
  8.          DBMS_OUTPUT.PUT_LINE(’僱員’||V_ENAME||’的工資被改爲’||TO_CHAR(V_SAL+P_RAISE));   
  9. COMMIT;   
  10.         EXCEPTION   
  11.          WHEN OTHERS THEN  
  12.         DBMS_OUTPUT.PUT_LINE(’發生錯誤,修改失敗!’);   
  13.         ROLLBACK;   
  14.         END;  

  1. CREATE OR REPLACE PROCEDURE CHANGE_SALARY(P_EMPNO IN NUMBER DEFAULT 7788,P_RAISE NUMBER DEFAULT 10)  
  2.         AS  
  3.          V_ENAME VARCHAR2(10);  
  4. V_SAL NUMBER(5);  
  5.         BEGIN  
  6.         SELECT ENAME,SAL INTO V_ENAME,V_SAL FROM EMP WHERE EMPNO=P_EMPNO;  
  7.          UPDATE EMP SET SAL=SAL+P_RAISE WHERE EMPNO=P_EMPNO;  
  8.          DBMS_OUTPUT.PUT_LINE(’僱員’||V_ENAME||‘的工資被改爲’||TO_CHAR(V_SAL+P_RAISE));  
  9. COMMIT;  
  10.         EXCEPTION  
  11.          WHEN OTHERS THEN  
  12.         DBMS_OUTPUT.PUT_LINE(’發生錯誤,修改失敗!’);  
  13.         ROLLBACK;  
  14.         END;  



執行結果爲:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟3:調用存儲過程,在輸入區中輸入以下語句並執行:
Sql代碼 複製代碼
  1. EXECUTE CHANGE_SALARY(7788,80)  

  1. EXECUTE CHANGE_SALARY(7788,80)  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員SCOTT的工資被改爲3080   

  1. 僱員SCOTT的工資被改爲3080   



說明:從執行結果可以看到,僱員SCOTT的工資已由原來的3000改爲3080。
參數的值由調用者傳遞,傳遞的參數的個數、類型和順序應該和定義的一致。如果順序不一致,可以採用以下調用方法。如上例,執行語句可以改爲:
 EXECUTE CHANGE_SALARY(P_RAISE=>80,P_EMPNO=>7788);
  可以看出傳遞參數的順序發生了變化,並且明確指出了參數名和要傳遞的值,=>運算符左側是參數名,右側是參數表達式,這種賦值方法的意義較清楚。
【練習1】創建插入僱員的存儲過程INSERT_EMP,並將僱員編號等作爲參數。
在設計存儲過程的時候,也可以爲參數設定默認值,這樣調用者就可以不傳遞或少傳遞參數了。
【訓練2】  調用存儲過程CHANGE_SALARY,不傳遞參數,使用默認參數值。
在SQL*Plus輸入區中輸入以下命令並執行:
Sql代碼 複製代碼
  1. EXECUTE CHANGE_SALARY  

  1. EXECUTE CHANGE_SALARY  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員SCOTT的工資被改爲3090   

  1. 僱員SCOTT的工資被改爲3090   



說明:在存儲過程的調用中沒有傳遞參數,而是採用了默認值7788和10,即默認僱員號爲7788,增加的工資爲10。
【訓練3】  使用OUT類型的參數返回存儲過程的結果。
步驟1:登錄SCOTT賬戶。
步驟2:在SQL*Plus輸入區中輸入並編譯以下存儲過程:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)   
  2.         AS  
  3.         BEGIN  
  4.         SELECT COUNT(*) INTO P_TOTAL FROM EMP;   
  5.         END;  

  1. CREATE OR REPLACE PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)  
  2.         AS  
  3.         BEGIN  
  4.         SELECT COUNT(*) INTO P_TOTAL FROM EMP;  
  5.         END;  



執行結果爲:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟3:輸入以下程序並執行:
Sql代碼 複製代碼
  1. DECLARE  
  2.         V_EMPCOUNT NUMBER;   
  3.         BEGIN  
  4.         EMP_COUNT(V_EMPCOUNT);   
  5.         DBMS_OUTPUT.PUT_LINE(’僱員總人數爲:’||V_EMPCOUNT);   
  6.         END;  

  1. DECLARE  
  2.         V_EMPCOUNT NUMBER;  
  3.         BEGIN  
  4.         EMP_COUNT(V_EMPCOUNT);  
  5.         DBMS_OUTPUT.PUT_LINE(’僱員總人數爲:’||V_EMPCOUNT);  
  6.         END;  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員總人數爲:14   
  2.         PL/SQL 過程已成功完成。  

  1. 僱員總人數爲:14  
  2.         PL/SQL 過程已成功完成。  



    說明:在存儲過程中定義了OUT類型的參數P_TOTAL,在主程序調用該存儲過程時,傳遞了參數V_EMPCOUNT。在存儲過程中的SELECT…INTO…語句中對P_TOTAL進行賦值,賦值結果由V_EMPCOUNT變量帶回給主程序並顯示。
以上程序要覆蓋同名的EMP_COUNT存儲過程,如果不使用OR REPLACE選項,就會出現以下錯誤:
Sql代碼 複製代碼
  1. ERROR 位於第 1 行:   
  2.         ORA-00955: 名稱已由現有對象使用。  

  1. ERROR 位於第 1 行:  
  2.         ORA-00955: 名稱已由現有對象使用。  



【練習2】創建存儲過程,使用OUT類型參數獲得僱員經理名。
【訓練4】  使用IN OUT類型的參數,給電話號碼增加區碼。
步驟1:登錄SCOTT賬戶。
步驟2:在SQL*Plus輸入區中輸入並編譯以下存儲過程:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PROCEDURE ADD_REGION(P_HPONE_NUM IN OUT VARCHAR2)   
  2.         AS  
  3.         BEGIN  
  4.          P_HPONE_NUM:=’0755-‘||P_HPONE_NUM;   
  5.         END;  

  1. CREATE OR REPLACE PROCEDURE ADD_REGION(P_HPONE_NUM IN OUT VARCHAR2)  
  2.         AS  
  3.         BEGIN  
  4.          P_HPONE_NUM:=’0755-‘||P_HPONE_NUM;  
  5.         END;  



執行結果爲:
Sql代碼 複製代碼
  1. 過程已創建。  

  1. 過程已創建。  



步驟3:輸入以下程序並執行:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2. DECLARE  
  3. V_PHONE_NUM VARCHAR2(15);   
  4. BEGIN  
  5. V_PHONE_NUM:=’26731092’;   
  6. ADD_REGION(V_PHONE_NUM);   
  7. DBMS_OUTPUT.PUT_LINE(’新的電話號碼:’||V_PHONE_NUM);   
  8. END;  

  1. SET SERVEROUTPUT ON  
  2. DECLARE  
  3. V_PHONE_NUM VARCHAR2(15);  
  4. BEGIN  
  5. V_PHONE_NUM:=’26731092’;  
  6. ADD_REGION(V_PHONE_NUM);  
  7. DBMS_OUTPUT.PUT_LINE(’新的電話號碼:’||V_PHONE_NUM);  
  8. END;  



顯示結果爲:
Sql代碼 複製代碼
  1. 新的電話號碼:0755-26731092   
  2.         PL/SQL 過程已成功完成。  

  1. 新的電話號碼:0755-26731092  
  2.         PL/SQL 過程已成功完成。  



說明:變量V_HPONE_NUM既用來向存儲過程傳遞舊電話號碼,也用來向主程序返回新號碼。新的號碼在原來基礎上增加了區號0755和-。
創建和刪除存儲函數
  創建函數,需要有CREATE PROCEDURE或CREATE ANY PROCEDURE的系統權限。該權限可由系統管理員授予。創建存儲函數的語法和創建存儲過程的類似,即
CREATE [OR REPLACE] FUNCTION 函數名[(參數[IN] 數據類型…)]
RETURN 數據類型
{AS|IS}
[說明部分]
BEGIN
可執行部分
RETURN (表達式)
[EXCEPTION
    錯誤處理部分]
END [函數名];
其中,參數是可選的,但只能是IN類型(IN關鍵字可以省略)。
在定義部分的RETURN 數據類型,用來表示函數的數據類型,也就是返回值的類型,此部分不可省略。
在可執行部分的RETURN(表達式),用來生成函數的返回值,其表達式的類型應該和定義部分說明的函數返回值的數據類型一致。在函數的執行部分可以有多個RETURN語句,但只有一個RETURN語句會被執行,一旦執行了RETURN語句,則函數結束並返回調用環境。
一個存儲函數在不需要時可以刪除,但刪除的人應是函數的創建者或者是擁有DROP ANY PROCEDURE系統權限的人。其語法如下:
DROP FUNCTION 函數名;
重新編譯一個存儲函數時,編譯的人應是函數的創建者或者擁有ALTER ANY PROCEDURE系統權限的人。重新編譯一個存儲函數的語法如下:
ALTER PROCEDURE 函數名 COMPILE;
函數的調用者應是函數的創建者或擁有EXECUTE ANY PROCEDURE系統權限的人,或是被函數的擁有者授予了函數執行權限的賬戶。函數的引用和存儲過程不同,函數要出現在程序體中,可以參加表達式的運算或單獨出現在表達式中,其形式如下:
變量名:=函數名(…)
【訓練1】  創建一個通過僱員編號返回僱員名稱的函數GET_EMP_NAME。
步驟1:登錄SCOTT賬戶。
步驟2:在SQL*Plus輸入區中輸入以下存儲函數並編譯:
Sql代碼 複製代碼
  1. CREATE OR REPLACE FUNCTION GET_EMP_NAME(P_EMPNO NUMBER DEFAULT 7788)   
  2.         RETURN VARCHAR2   
  3.         AS  
  4.          V_ENAME VARCHAR2(10);   
  5.         BEGIN  
  6.         ELECT ENAME INTO V_ENAME FROM EMP WHERE EMPNO=P_EMPNO;   
  7. RETURN(V_ENAME);   
  8. EXCEPTION   
  9.  WHEN NO_DATA_FOUND THEN  
  10.   DBMS_OUTPUT.PUT_LINE(’沒有該編號僱員!’);   
  11.   RETURN (NULL);   
  12.  WHEN TOO_MANY_ROWS THEN  
  13.   DBMS_OUTPUT.PUT_LINE(’有重複僱員編號!’);   
  14.   RETURN (NULL);   
  15.  WHEN OTHERS THEN  
  16.   DBMS_OUTPUT.PUT_LINE(’發生其他錯誤!’);   
  17.   RETURN (NULL);   
  18. END;  

  1. CREATE OR REPLACE FUNCTION GET_EMP_NAME(P_EMPNO NUMBER DEFAULT 7788)  
  2.         RETURN VARCHAR2  
  3.         AS  
  4.          V_ENAME VARCHAR2(10);  
  5.         BEGIN  
  6.         ELECT ENAME INTO V_ENAME FROM EMP WHERE EMPNO=P_EMPNO;  
  7. RETURN(V_ENAME);  
  8. EXCEPTION  
  9.  WHEN NO_DATA_FOUND THEN  
  10.   DBMS_OUTPUT.PUT_LINE(’沒有該編號僱員!’);  
  11.   RETURN (NULL);  
  12.  WHEN TOO_MANY_ROWS THEN  
  13.   DBMS_OUTPUT.PUT_LINE(’有重複僱員編號!’);  
  14.   RETURN (NULL);  
  15.  WHEN OTHERS THEN  
  16.   DBMS_OUTPUT.PUT_LINE(’發生其他錯誤!’);  
  17.   RETURN (NULL);  
  18. END;  



步驟3:調用該存儲函數,輸入並執行以下程序:
Sql代碼 複製代碼
  1. BEGIN  
  2.         DBMS_OUTPUT.PUT_LINE(’僱員7369的名稱是:’|| GET_EMP_NAME(7369));   
  3.          DBMS_OUTPUT.PUT_LINE(’僱員7839的名稱是:’|| GET_EMP_NAME(7839));   
  4.         END;  

  1. BEGIN  
  2.         DBMS_OUTPUT.PUT_LINE(’僱員7369的名稱是:’|| GET_EMP_NAME(7369));  
  3.          DBMS_OUTPUT.PUT_LINE(’僱員7839的名稱是:’|| GET_EMP_NAME(7839));  
  4.         END;  



顯示結果爲:
Sql代碼 複製代碼
  1. 僱員7369的名稱是:SMITH   
  2.         僱員7839的名稱是:KING   
  3.         PL/SQL 過程已成功完成。  

  1. 僱員7369的名稱是:SMITH  
  2.         僱員7839的名稱是:KING  
  3.         PL/SQL 過程已成功完成。  



說明:函數的調用直接出現在程序的DBMS_OUTPUT.PUT_LINE語句中,作爲字符串表達式的一部分。如果輸入了錯誤的僱員編號,就會在函數的錯誤處理部分輸出錯誤信息。試修改僱員編號,重新運行調用部分。
【練習1】創建一個通過部門編號返回部門名稱的存儲函數GET_DEPT_NAME。
   【練習2】將函數的執行權限授予STUDENT賬戶,然後登錄STUDENT賬戶調用。
存儲過程和函數的查看
可以通過對數據字典的訪問來查詢存儲過程或函數的有關信息,如果要查詢當前用戶的存儲過程或函數的源代碼,可以通過對USER_SOURCE數據字典視圖的查詢得到。USER_SOURCE的結構如下:
Sql代碼 複製代碼
  1. DESCRIBE USER_SOURCE  

  1. DESCRIBE USER_SOURCE  



結果爲:
Sql代碼 複製代碼
  1. 名稱                                      是否爲空? 類型   
  2.         ————————————————————- ————- ———————–  
  3.  NAME                                               VARCHAR2(30)   
  4.  TYPE                                               VARCHAR2(12)   
  5.  LINE                                               NUMBER   
  6.  TEXT                                               VARCHAR2(4000)  

  1. 名稱                                      是否爲空? 類型  
  2.         ————————————————————- ————- ———————–  
  3.  NAME                                               VARCHAR2(30)  
  4.  TYPE                                               VARCHAR2(12)  
  5.  LINE                                               NUMBER  
  6.  TEXT                                               VARCHAR2(4000)  



  說明:裏面按行存放着過程或函數的腳本,NAME是過程或函數名,TYPE 代表類型(PROCEDURE或FUNCTION),LINE是行號,TEXT 爲腳本。
【訓練1】  查詢過程EMP_COUNT的腳本。
在SQL*Plus中輸入並執行如下查詢:
Sql代碼 複製代碼
  1. select TEXT  from user_source WHERE NAME=’EMP_COUNT’;  

  1. select TEXT  from user_source WHERE NAME=‘EMP_COUNT’;  



結果爲:
Sql代碼 複製代碼
  1. TEXT   
  2. ——————————————————————————–  
  3. PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)   
  4. AS  
  5. BEGIN  
  6.  SELECT COUNT(*) INTO P_TOTAL FROM EMP;   
  7. END;  

  1. TEXT  
  2. ——————————————————————————–  
  3. PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)  
  4. AS  
  5. BEGIN  
  6.  SELECT COUNT(*) INTO P_TOTAL FROM EMP;  
  7. END;  



【訓練2】  查詢過程GET_EMP_NAME的參數。
在SQL*Plus中輸入並執行如下查詢:
Sql代碼 複製代碼
  1. DESCRIBE GET_EMP_NAME  

  1. DESCRIBE GET_EMP_NAME  



結果爲:
Sql代碼 複製代碼
  1. FUNCTION GET_EMP_NAME RETURNS VARCHAR2   
  2.         參數名稱            類型          輸入/輸出默認值?   
  3.         —————————————– ———————————– —————– ————-  
  4.         P_EMPNO             NUMBER(4) IN     DEFAULT  

  1. FUNCTION GET_EMP_NAME RETURNS VARCHAR2  
  2.         參數名稱            類型          輸入/輸出默認值?  
  3.         —————————————– ———————————– —————– ————-  
  4.         P_EMPNO             NUMBER(4) IN     DEFAULT  



【訓練3】  在發生編譯錯誤時,顯示錯誤。
Sql代碼 複製代碼
  1. SHOW ERRORS  

  1. SHOW ERRORS  



以下是一段編譯錯誤顯示:
Sql代碼 複製代碼
  1. LINE/COL ERROR   
  2.         ————- —————————————————————–  
  3.         4/2       PL/SQL: SQL Statement ignored   
  4.         4/36      PLS-00201: 必須說明標識符 ’EMPP’  

  1. LINE/COL ERROR  
  2.         ————- —————————————————————–  
  3.         4/2       PL/SQL: SQL Statement ignored  
  4.         4/36      PLS-00201: 必須說明標識符 ’EMPP’  



  說明:查詢一個存儲過程或函數是否是有效狀態(即編譯成功),可以使用數據字典USER_OBJECTS的STATUS列。
【訓練4】  查詢EMP_LIST存儲過程是否可用:
Sql代碼 複製代碼
  1. SELECT STATUS FROM USER_OBJECTS WHERE OBJECT_NAME=’EMP_LIST’;  

  1. SELECT STATUS FROM USER_OBJECTS WHERE OBJECT_NAME=‘EMP_LIST’;  



結果爲:
Sql代碼 複製代碼
  1. STATUS   
  2.         ————  
  3.         VALID  

  1. STATUS  
  2.         ————  
  3.         VALID  



說明:VALID表示該存儲過程有效(即通過編譯),INVALID表示存儲過程無效或需要重新編譯。當Oracle調用一個無效的存儲過程或函數時,首先試圖對其進行編譯,如果編譯成功則將狀態置成VALID並執行,否則給出錯誤信息。
當一個存儲過程編譯成功,狀態變爲VALID,會不會在某些情況下變成INVALID。結論是完全可能的。比如一個存儲過程中包含對錶的查詢,如果表被修改或刪除,存儲過程就會變成無效INVALID。所以要注意存儲過程和函數對其他對象的依賴關係。
如果要檢查存儲過程或函數的依賴性,可以通過查詢數據字典USER_DENPENDENCIES來確定,該表結構如下:
Sql代碼 複製代碼
  1. DESCRIBE USER_DEPENDENCIES;  

  1. DESCRIBE USER_DEPENDENCIES;  



結果:
Sql代碼 複製代碼
  1. 名稱                     是否爲空? 類型   
  2.         ————————————————————– ————- —————————-  
  3.          NAME            NOT NULL   VARCHAR2(30)   
  4.          TYPE                       VARCHAR2(12)   
  5.         REFERENCED_OWNER                                VARCHAR2(30)   
  6.  REFERENCED_NAME                                VARCHAR2(64)   
  7.  REFERENCED_TYPE                                VARCHAR2(12)   
  8. REFERENCED_LINK_NAME                            VARCHAR2(128)   
  9.         SCHEMAID                                        NUMBER   
  10.          DEPENDENCY_TYPE                                VARCHAR2(4)  

  1. 名稱                     是否爲空? 類型  
  2.         ————————————————————– ————- —————————-  
  3.          NAME            NOT NULL   VARCHAR2(30)  
  4.          TYPE                       VARCHAR2(12)  
  5.         REFERENCED_OWNER                                VARCHAR2(30)  
  6.  REFERENCED_NAME                                VARCHAR2(64)  
  7.  REFERENCED_TYPE                                VARCHAR2(12)  
  8. REFERENCED_LINK_NAME                            VARCHAR2(128)  
  9.         SCHEMAID                                        NUMBER  
  10.          DEPENDENCY_TYPE                                VARCHAR2(4)  



  說明:NAME爲實體名,TYPE爲實體類型,REFERENCED_OWNER爲涉及到的實體擁有者賬戶,REFERENCED_NAME爲涉及到的實體名,REFERENCED_TYPE 爲涉及到的實體類型。
【訓練5】  查詢EMP_LIST存儲過程的依賴性。
Sql代碼 複製代碼
  1. SELECT REFERENCED_NAME,REFERENCED_TYPE FROM USER_DEPENDENCIES WHERE NAME=’EMP_LIST’;  

  1. SELECT REFERENCED_NAME,REFERENCED_TYPE FROM USER_DEPENDENCIES WHERE NAME=‘EMP_LIST’;  



執行結果:
Sql代碼 複製代碼
  1. REFERENCED_NAME                                         REFERENCED_TYPE   
  2.         —————————————————————————————— —————————-  
  3. STANDARD                                                PACKAGE   
  4.         SYS_STUB_FOR_PURITY_ANALYSIS                            PACKAGE   
  5.         DBMS_OUTPUT                                                 PACKAGE   
  6.         DBMS_OUTPUT                                             SYNONYM   
  7. DBMS_OUTPUT                      NON-EXISTENT   
  8.         EMP                                                         TABLE  
  9.         EMP_COUNT                                                   PROCEDURE  

  1. REFERENCED_NAME                                         REFERENCED_TYPE  
  2.         —————————————————————————————— —————————-  
  3. STANDARD                                                PACKAGE  
  4.         SYS_STUB_FOR_PURITY_ANALYSIS                            PACKAGE  
  5.         DBMS_OUTPUT                                                 PACKAGE  
  6.         DBMS_OUTPUT                                             SYNONYM  
  7. DBMS_OUTPUT                      NON-EXISTENT  
  8.         EMP                                                         TABLE  
  9.         EMP_COUNT                                                   PROCEDURE  



  說明:可以看出存儲過程EMP_LIST依賴一些系統包、EMP表和EMP_COUNT存儲過程。如果刪除了EMP表或EMP_COUNT存儲過程,EMP_LIST將變成無效。
還有一種情況需要我們注意:如果一個用戶A被授予執行屬於用戶B的一個存儲過程的權限,在用戶B的存儲過程中,訪問到用戶C的表,用戶B被授予訪問用戶C的表的權限,但用戶A沒有被授予訪問用戶C表的權限,那麼用戶A調用用戶B的存儲過程是失敗的還是成功的呢?答案是成功的。如果讀者有興趣,不妨進行一下實際測試。


包的概念和組成
包是用來存儲相關程序結構的對象,它存儲於數據字典中。包由兩個分離的部分組成:包頭(PACKAGE)和包體(PACKAGE BODY)。包頭是包的說明部分,是對外的操作接口,對應用是可見的;包體是包的代碼和實現部分,對應用來說是不可見的黑盒。
包中可以包含的程序結構如下所示。
Sql代碼 複製代碼
  1. 過程(PROCUDURE)   帶參數的命名的程序模塊   
  2. 函數(FUNCTION)    帶參數、具有返回值的命名的程序模塊   
  3. 變量(VARIABLE)    存儲變化的量的存儲單元   
  4. 常量(CONSTANT)    存儲不變的量的存儲單元   
  5. 遊標(CURSOR)  用戶定義的數據操作緩存區,在可執行部分使用   
  6. 類型(TYPE)    用戶定義的新的結構類型   
  7. 異常(EXCEPTION)   在標準包中定義或由用戶自定義,用於處理程序錯誤  

  1. 過程(PROCUDURE)   帶參數的命名的程序模塊  
  2. 函數(FUNCTION)    帶參數、具有返回值的命名的程序模塊  
  3. 變量(VARIABLE)    存儲變化的量的存儲單元  
  4. 常量(CONSTANT)    存儲不變的量的存儲單元  
  5. 遊標(CURSOR)  用戶定義的數據操作緩存區,在可執行部分使用  
  6. 類型(TYPE)    用戶定義的新的結構類型  
  7. 異常(EXCEPTION)   在標準包中定義或由用戶自定義,用於處理程序錯誤  



說明部分可以出現在包的三個不同的部分:出現在包頭中的稱爲公有元素,出現在包體中的稱爲私有元素,出現在包體的過程(或函數)中的稱爲局部變量。它們的性質有所不同,如下所示。
Sql代碼 複製代碼
  1. 公有元素(PUBLIC)    在包頭中說明,在包體中具體定義 在包外可見並可以訪問,對整個應用的全過程有效   
  2. 私有元素(PRIVATE)   在包體的說明部分說明  只能被包內部的其他部分訪問   
  3. 局部變量(LOCAL) 在過程或函數的說明部分說明   只能在定義變量的過程或函數中使用  

  1. 公有元素(PUBLIC)    在包頭中說明,在包體中具體定義 在包外可見並可以訪問,對整個應用的全過程有效  
  2. 私有元素(PRIVATE)   在包體的說明部分說明  只能被包內部的其他部分訪問  
  3. 局部變量(LOCAL) 在過程或函數的說明部分說明   只能在定義變量的過程或函數中使用  



在包體中出現的過程或函數,如果需要對外公用,就必須在包頭中說明,包頭中的說明應該和包體中的說明一致。
包有以下優點:
* 包可以方便地將存儲過程和函數組織到一起,每個包又是相互獨立的。在不同的包中,過程、函數都可以重名,這解決了在同一個用戶環境中命名的衝突問題。
* 包增強了對存儲過程和函數的安全管理,對整個包的訪問權只需一次授予。
  * 在同一個會話中,公用變量的值將被保留,直到會話結束。
* 區分了公有過程和私有過程,包體的私有過程增加了過程和函數的保密性。
* 包在被首次調用時,就作爲一個整體被全部調入內存,減少了多次訪問過程或函數的I/O次數。
創建包和包體
包由包頭和包體兩部分組成,包的創建應該先創建包頭部分,然後創建包體部分。創建、刪除和編譯包的權限同創建、刪除和編譯存儲過程的權限相同。
創建包頭的簡要語句如下:
CREATE [OR REPLACE] PACKAGE 包名
{IS|AS}
公有變量定義
公有類型定義
公有遊標定義
公有異常定義
函數說明
過程說明
END;
創建包體的簡要語法如下:
CREATE [OR REPLACE] PACKAGE BODY 包名
{IS|AS}
私有變量定義
私有類型定義
私有遊標定義
私有異常定義
函數定義
過程定義
END;
包的其他操作命令包括:
刪除包頭:
DROP PACKAGE 包頭名
刪除包體:
DROP PACKAGE BODY 包體名
重新編譯包頭:
ALTER PACKAGE 包名 COMPILE PACKAGE
重新編譯包體:
ALTER PACKAGE 包名 COMPILE PACKAGE BODY
在包頭中說明的對象可以在包外調用,調用的方法和調用單獨的過程或函數的方法基本相同,惟一的區別就是要在調用的過程或函數名前加上包的名字(中間用“.”分隔)。但要注意,不同的會話將單獨對包的公用變量進行初始化,所以不同的會話對包的調用屬於不同的應用。
系統包
Oracle預定義了很多標準的系統包,這些包可以在應用中直接使用,比如在訓練中我們使用的DBMS_OUTPUT包,就是系統包。PUT_LINE是該包的一個函數。常用系統包下所示。
Sql代碼 複製代碼
  1. DBMS_OUTPUT 在SQL*Plus環境下輸出信息   
  2. DBMS_DDL    編譯過程函數和包   
  3. DBMS_SESSION    改變用戶的會話,初始化包等   
  4. DBMS_TRANSACTION    控制數據庫事務   
  5. DBMS_MAIL   連接Oracle*Mail   
  6. DBMS_LOCK   進行復雜的鎖機制管理   
  7. DBMS_ALERT  識別數據庫事件告警   
  8. DBMS_PIPE   通過管道在會話間傳遞信息   
  9. DBMS_JOB    管理Oracle的作業   
  10. DBMS_LOB    操縱大對象   
  11. DBMS_SQL    執行動態SQL語句  

  1. DBMS_OUTPUT 在SQL*Plus環境下輸出信息  
  2. DBMS_DDL    編譯過程函數和包  
  3. DBMS_SESSION    改變用戶的會話,初始化包等  
  4. DBMS_TRANSACTION    控制數據庫事務  
  5. DBMS_MAIL   連接Oracle*Mail  
  6. DBMS_LOCK   進行復雜的鎖機制管理  
  7. DBMS_ALERT  識別數據庫事件告警  
  8. DBMS_PIPE   通過管道在會話間傳遞信息  
  9. DBMS_JOB    管理Oracle的作業  
  10. DBMS_LOB    操縱大對象  
  11. DBMS_SQL    執行動態SQL語句  



包的應用
在SQL*Plus環境下,包和包體可以分別編譯,也可以一起編譯。如果分別編譯,則要先編譯包頭,後編譯包體。如果在一起編譯,則包頭寫在前,包體在後,中間用“/”分隔。
可以將已經存在的存儲過程或函數添加到包中,方法是去掉過程或函數創建語句的CREATE OR REPLACE部分,將存儲過程或函數複製到包體中 ,然後重新編譯即可。
   如果需要將私有過程或函數變成共有過程或函數的話,將過程或函數說明部分複製到包頭說明部分,然後重新編譯就可以了。
【訓練1】  創建管理僱員信息的包EMPLOYE,它具有從EMP表獲得僱員信息,修改僱員名稱,修改僱員工資和寫回EMP表的功能。
步驟1:登錄SCOTT賬戶,輸入以下代碼並編譯:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PACKAGE EMPLOYE –包頭部分   
  2.         IS  
  3.  PROCEDURE SHOW_DETAIL;    
  4.  PROCEDURE GET_EMPLOYE(P_EMPNO NUMBER);    
  5.  PROCEDURE SAVE_EMPLOYE;    
  6.  PROCEDURE CHANGE_NAME(P_NEWNAME VARCHAR2);    
  7. PROCEDURE CHANGE_SAL(P_NEWSAL NUMBER);    
  8.         END EMPLOYE;   
  9.         /   
  10.         CREATE OR REPLACE PACKAGE BODY EMPLOYE –包體部分   
  11.         IS  
  12.  EMPLOYE EMP%ROWTYPE;   
  13.         ————– 顯示僱員信息 —————  
  14.         PROCEDURE SHOW_DETAIL   
  15.         AS  
  16.         BEGIN  
  17. DBMS_OUTPUT.PUT_LINE(‘—– 僱員信息 —–’);     
  18.         DBMS_OUTPUT.PUT_LINE(’僱員編號:’||EMPLOYE.EMPNO);   
  19.         DBMS_OUTPUT.PUT_LINE(’僱員名稱:’||EMPLOYE.ENAME);   
  20.           DBMS_OUTPUT.PUT_LINE(’僱員職務:’||EMPLOYE.JOB);   
  21.          DBMS_OUTPUT.PUT_LINE(’僱員工資:’||EMPLOYE.SAL);   
  22.          DBMS_OUTPUT.PUT_LINE(’部門編號:’||EMPLOYE.DEPTNO);   
  23.         END SHOW_DETAIL;   
  24. —————– 從EMP表取得一個僱員 ——————–  
  25.          PROCEDURE GET_EMPLOYE(P_EMPNO NUMBER)   
  26.         AS  
  27.         BEGIN  
  28.         SELECT * INTO EMPLOYE FROM EMP WHERE    EMPNO=P_EMPNO;   
  29.         DBMS_OUTPUT.PUT_LINE(’獲取僱員’||EMPLOYE.ENAME||’信息成功’);   
  30.          EXCEPTION   
  31.          WHEN OTHERS THEN  
  32.            DBMS_OUTPUT.PUT_LINE(’獲取僱員信息發生錯誤!’);   
  33.         END GET_EMPLOYE;   
  34. ———————- 保存僱員到EMP表 ————————–  
  35.         PROCEDURE SAVE_EMPLOYE   
  36.         AS  
  37.         BEGIN  
  38.         UPDATE EMP SET ENAME=EMPLOYE.ENAME, SAL=EMPLOYE.SAL WHERE EMPNO=   
  39.     EMPLOYE.EMPNO;   
  40.      DBMS_OUTPUT.PUT_LINE(’僱員信息保存完成!’);   
  41.         END SAVE_EMPLOYE;   
  42. —————————- 修改僱員名稱 ——————————  
  43.         PROCEDURE CHANGE_NAME(P_NEWNAME VARCHAR2)   
  44.          AS  
  45.         BEGIN  
  46.          EMPLOYE.ENAME:=P_NEWNAME;   
  47.          DBMS_OUTPUT.PUT_LINE(’修改名稱完成!’);   
  48.         END CHANGE_NAME;   
  49. —————————- 修改僱員工資 ————————–  
  50.         PROCEDURE CHANGE_SAL(P_NEWSAL NUMBER)   
  51.         AS  
  52.         BEGIN  
  53.          EMPLOYE.SAL:=P_NEWSAL;   
  54.          DBMS_OUTPUT.PUT_LINE(’修改工資完成!’);   
  55.         END CHANGE_SAL;   
  56.         END EMPLOYE;  

  1. CREATE OR REPLACE PACKAGE EMPLOYE –包頭部分   
  2.         IS  
  3.  PROCEDURE SHOW_DETAIL;   
  4.  PROCEDURE GET_EMPLOYE(P_EMPNO NUMBER);   
  5.  PROCEDURE SAVE_EMPLOYE;   
  6.  PROCEDURE CHANGE_NAME(P_NEWNAME VARCHAR2);   
  7. PROCEDURE CHANGE_SAL(P_NEWSAL NUMBER);   
  8.         END EMPLOYE;  
  9.         /  
  10.         CREATE OR REPLACE PACKAGE BODY EMPLOYE –包體部分   
  11.         IS  
  12.  EMPLOYE EMP%ROWTYPE;  
  13.         ————– 顯示僱員信息 —————  
  14.         PROCEDURE SHOW_DETAIL  
  15.         AS  
  16.         BEGIN  
  17. DBMS_OUTPUT.PUT_LINE(‘—– 僱員信息 —–’);     
  18.         DBMS_OUTPUT.PUT_LINE(’僱員編號:’||EMPLOYE.EMPNO);  
  19.         DBMS_OUTPUT.PUT_LINE(’僱員名稱:’||EMPLOYE.ENAME);  
  20.           DBMS_OUTPUT.PUT_LINE(’僱員職務:’||EMPLOYE.JOB);  
  21.          DBMS_OUTPUT.PUT_LINE(’僱員工資:’||EMPLOYE.SAL);  
  22.          DBMS_OUTPUT.PUT_LINE(’部門編號:’||EMPLOYE.DEPTNO);  
  23.         END SHOW_DETAIL;  
  24. —————– 從EMP表取得一個僱員 ——————–  
  25.          PROCEDURE GET_EMPLOYE(P_EMPNO NUMBER)  
  26.         AS  
  27.         BEGIN  
  28.         SELECT * INTO EMPLOYE FROM EMP WHERE    EMPNO=P_EMPNO;  
  29.         DBMS_OUTPUT.PUT_LINE(’獲取僱員’||EMPLOYE.ENAME||‘信息成功’);  
  30.          EXCEPTION  
  31.          WHEN OTHERS THEN  
  32.            DBMS_OUTPUT.PUT_LINE(’獲取僱員信息發生錯誤!’);  
  33.         END GET_EMPLOYE;  
  34. ———————- 保存僱員到EMP表 ————————–  
  35.         PROCEDURE SAVE_EMPLOYE  
  36.         AS  
  37.         BEGIN  
  38.         UPDATE EMP SET ENAME=EMPLOYE.ENAME, SAL=EMPLOYE.SAL WHERE EMPNO=  
  39.     EMPLOYE.EMPNO;  
  40.      DBMS_OUTPUT.PUT_LINE(’僱員信息保存完成!’);  
  41.         END SAVE_EMPLOYE;  
  42. —————————- 修改僱員名稱 ——————————  
  43.         PROCEDURE CHANGE_NAME(P_NEWNAME VARCHAR2)  
  44.          AS  
  45.         BEGIN  
  46.          EMPLOYE.ENAME:=P_NEWNAME;  
  47.          DBMS_OUTPUT.PUT_LINE(’修改名稱完成!’);  
  48.         END CHANGE_NAME;  
  49. —————————- 修改僱員工資 ————————–  
  50.         PROCEDURE CHANGE_SAL(P_NEWSAL NUMBER)  
  51.         AS  
  52.         BEGIN  
  53.          EMPLOYE.SAL:=P_NEWSAL;  
  54.          DBMS_OUTPUT.PUT_LINE(’修改工資完成!’);  
  55.         END CHANGE_SAL;  
  56.         END EMPLOYE;  



步驟2:獲取僱員7788的信息:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2.         EXECUTE EMPLOYE.GET_EMPLOYE(7788);  

  1. SET SERVEROUTPUT ON  
  2.         EXECUTE EMPLOYE.GET_EMPLOYE(7788);  



結果爲:
Sql代碼 複製代碼
  1. 獲取僱員SCOTT信息成功   
  2.         PL/SQL 過程已成功完成。  

  1. 獲取僱員SCOTT信息成功  
  2.         PL/SQL 過程已成功完成。  



步驟3:顯示僱員信息:
Sql代碼 複製代碼
  1. EXECUTE EMPLOYE.SHOW_DETAIL;  

  1. EXECUTE EMPLOYE.SHOW_DETAIL;  



結果爲:
Sql代碼 複製代碼
  1. —————— 僱員信息 ——————  
  2.         僱員編號:7788   
  3.         僱員名稱:SCOTT   
  4.         僱員職務:ANALYST   
  5.         僱員工資:3000   
  6.         部門編號:20   
  7.         PL/SQL 過程已成功完成。  

  1. —————— 僱員信息 ——————  
  2.         僱員編號:7788  
  3.         僱員名稱:SCOTT  
  4.         僱員職務:ANALYST  
  5.         僱員工資:3000  
  6.         部門編號:20  
  7.         PL/SQL 過程已成功完成。  



步驟4:修改僱員工資:
Sql代碼 複製代碼
  1. EXECUTE EMPLOYE.CHANGE_SAL(3800);  

  1. EXECUTE EMPLOYE.CHANGE_SAL(3800);  



結果爲:
Sql代碼 複製代碼
  1. 修改工資完成!   
  2.         PL/SQL 過程已成功完成。  

  1. 修改工資完成!  
  2.         PL/SQL 過程已成功完成。  



步驟5:將修改的僱員信息存入EMP表
Sql代碼 複製代碼
  1. EXECUTE EMPLOYE.SAVE_EMPLOYE;  

  1. EXECUTE EMPLOYE.SAVE_EMPLOYE;  



結果爲:
Sql代碼 複製代碼
  1. 僱員信息保存完成!   
  2.         PL/SQL 過程已成功完成。  

  1. 僱員信息保存完成!  
  2.         PL/SQL 過程已成功完成。  



說明:該包完成將EMP表中的某個僱員的信息取入內存記錄變量,在記錄變量中進行修改編輯,在確認顯示信息正確後寫回EMP表的功能。記錄變量EMPLOYE用來存儲取得的僱員信息,定義爲私有變量,只能被包的內部模塊訪問。
  【練習1】爲包增加修改僱員職務和部門編號的功能。

階段訓練
下面的訓練通過定義和創建完整的包EMP_PK並綜合運用本章的知識,完成對僱員表的插入、刪除等功能,包中的主要元素解釋如下所示。
Sql代碼 複製代碼
  1. 程序結構    類  型    說    明   
  2. V_EMP_COUNT 公有變量    跟蹤僱員的總人數變化,插入、刪除僱員的同時修改該變量的值   
  3. INIT    公有過程    對包進行初始化,初始化僱員人數和工資修改的上、下限   
  4. LIST_EMP    公有過程    顯示僱員列表   
  5. INSERT_EMP  公有過程    通過編號插入新僱員   
  6. DELETE_EMP  公有過程    通過編號刪除僱員   
  7. CHANGE_EMP_SAL  公有過程    通過編號修改僱員工資   
  8. V_MESSAGE   私有變量    存放準備輸出的信息   
  9. C_MAX_SAL   私有變量    對工資修改的上限   
  10. C_MIN_SAL   私有變量    對工資修改的下限   
  11. SHOW_MESSAGE    私有過程    顯示私有變量V_MESSAGE中的信息   
  12. EXIST_EMP   私有函數    判斷某個編號的僱員是否存在,該函數被INSERT_EMP、DELETE_EMP和CHANGE_EMP_SAL等過程調用  

  1. 程序結構    類  型    說    明  
  2. V_EMP_COUNT 公有變量    跟蹤僱員的總人數變化,插入、刪除僱員的同時修改該變量的值  
  3. INIT    公有過程    對包進行初始化,初始化僱員人數和工資修改的上、下限  
  4. LIST_EMP    公有過程    顯示僱員列表  
  5. INSERT_EMP  公有過程    通過編號插入新僱員  
  6. DELETE_EMP  公有過程    通過編號刪除僱員  
  7. CHANGE_EMP_SAL  公有過程    通過編號修改僱員工資  
  8. V_MESSAGE   私有變量    存放準備輸出的信息  
  9. C_MAX_SAL   私有變量    對工資修改的上限  
  10. C_MIN_SAL   私有變量    對工資修改的下限  
  11. SHOW_MESSAGE    私有過程    顯示私有變量V_MESSAGE中的信息  
  12. EXIST_EMP   私有函數    判斷某個編號的僱員是否存在,該函數被INSERT_EMP、DELETE_EMP和CHANGE_EMP_SAL等過程調用  



【訓練1】  完整的僱員包EMP_PK的創建和應用。
步驟1:在SQL*Plus中登錄SCOTT賬戶,輸入以下包頭和包體部分,按“執行”按鈕編譯:
Sql代碼 複製代碼
  1. CREATE OR REPLACE PACKAGE EMP_PK    
  2.         –包頭部分   
  3.         IS  
  4.         V_EMP_COUNT NUMBER(5);                 
  5.         –僱員人數  
  6.         PROCEDURE INIT(P_MAX NUMBER,P_MIN NUMBER);  –初始化  
  7.         PROCEDURE LIST_EMP;                        
  8.         –顯示僱員列表  
  9. PROCEDURE INSERT_EMP(P_EMPNO        NUMBER,P_ENAMEVARCHAR2,P_JOB VARCHAR2,   
  10.         P_SAL NUMBER);                         
  11.         –插入僱員  
  12.         PROCEDURE DELETE_EMP(P_EMPNO NUMBER);       –刪除僱員  
  13.          PROCEDURE CHANGE_EMP_SAL(P_EMPNO NUMBER,P_SAL NUMBER);    
  14.         –修改僱員工資  
  15.         END EMP_PK;   
  16.         /CREATE OR REPLACE PACKAGE BODY EMP_PK   
  17.          –包體部分   
  18.         IS  
  19.         V_MESSAGE VARCHAR2(50); –顯示信息  
  20. V_MAX_SAL NUMBER(7); –工資上限  
  21.         V_MIN_SAL NUMBER(7); –工資下限  
  22.         FUNCTION EXIST_EMP(P_EMPNO NUMBER)  RETURN  BOOLEAN; –判斷僱員是否存在函數  
  23.         PROCEDURE SHOW_MESSAGE; –顯示信息過程  
  24.         ——————————- 初始化過程 —————————-  
  25.         PROCEDURE INIT(P_MAX NUMBER,P_MIN NUMBER)    
  26.         IS    
  27.         BEGIN  
  28.          SELECT COUNT(*) INTO V_EMP_COUNT FROM EMP;   
  29. V_MAX_SAL:=P_MAX;   
  30.          V_MIN_SAL:=P_MIN;   
  31.          V_MESSAGE:=’初始化過程已經完成!’;   
  32.          SHOW_MESSAGE;    
  33.         END INIT;   
  34. —————————- 顯示僱員列表過程 ———————  
  35.         PROCEDURE LIST_EMP    
  36.          IS    
  37.         BEGIN  
  38. DBMS_OUTPUT.PUT_LINE(’姓名       職務      工資’);   
  39.         FOR emp_rec IN (SELECT * FROM EMP)   
  40.         LOOP   
  41.     DBMS_OUTPUT.PUT_LINE(RPAD(emp_rec.ename,10,)||RPAD(emp_rec.job,10,’ ’)||TO_CHAR(emp_rec.sal));   
  42.          END LOOP;   
  43.          DBMS_OUTPUT.PUT_LINE(’僱員總人數’||V_EMP_COUNT);   
  44.         END LIST_EMP;   
  45. —————————– 插入僱員過程 —————————–  
  46.         PROCEDUREINSERT_EMP(P_EMPNO     NUMBER,P_ENAMEVARCHAR2,P_JOB    VARCHAR2,P_SAL NUMBER)   
  47.          IS    
  48.         BEGIN  
  49.         IF NOT EXIST_EMP(P_EMPNO) THEN  
  50.         INSERT INTO EMP(EMPNO,ENAME,JOB,SAL)        VALUES(P_EMPNO,P_ENAME,P_JOB,P_SAL);   
  51.         COMMIT;    
  52.         V_EMP_COUNT:=V_EMP_COUNT+1;   
  53.         V_MESSAGE:=’僱員’||P_EMPNO||’已插入!’;   
  54.         ELSE  
  55. V_MESSAGE:=’僱員’||P_EMPNO||’已存在,不能插入!’;   
  56.       END IF;   
  57.      SHOW_MESSAGE;    
  58.      EXCEPTION   
  59.     WHEN OTHERS THEN  
  60.      V_MESSAGE:=’僱員’||P_EMPNO||’插入失敗!’;   
  61.      SHOW_MESSAGE;   
  62.      END INSERT_EMP;   
  63. ————————— 刪除僱員過程 ——————–  
  64.          PROCEDURE DELETE_EMP(P_EMPNO NUMBER)    
  65.         IS    
  66.         BEGIN    
  67.         IF EXIST_EMP(P_EMPNO) THEN  
  68.         DELETE FROM EMP WHERE EMPNO=P_EMPNO;   
  69.         COMMIT;   
  70.          V_EMP_COUNT:=V_EMP_COUNT-1;   
  71.          V_MESSAGE:=’僱員’||P_EMPNO||’已刪除!’;   
  72.          ELSE  
  73. V_MESSAGE:=’僱員’||P_EMPNO||’不存在,不能刪除!’;   
  74.     END IF;   
  75.     SHOW_MESSAGE;   
  76.      EXCEPTION   
  77.      WHEN OTHERS THEN  
  78.      V_MESSAGE:=’僱員’||P_EMPNO||’刪除失敗!’;   
  79.      SHOW_MESSAGE;   
  80.     END DELETE_EMP;   
  81. ————————————— 修改僱員工資過程 ————————————  
  82.         PROCEDURE CHANGE_EMP_SAL(P_EMPNO NUMBER,P_SAL NUMBER)    
  83.          IS    
  84.          BEGIN    
  85.          IF (P_SAL>V_MAX_SAL OR P_SAL<V_MIN_SAL) THEN  
  86.          V_MESSAGE:=’工資超出修改範圍!’;   
  87.         ELSIF NOT EXIST_EMP(P_EMPNO) THEN  
  88.         V_MESSAGE:=’僱員’||P_EMPNO||’不存在,不能修改工資!’;   
  89. ELSE  
  90.          UPDATE EMP SET SAL=P_SAL WHERE EMPNO=P_EMPNO;   
  91.         COMMIT;   
  92.         V_MESSAGE:=’僱員’||P_EMPNO||’工資已經修改!’;   
  93.         END IF;   
  94.         SHOW_MESSAGE;   
  95.         EXCEPTION   
  96.          WHEN OTHERS THEN  
  97.          V_MESSAGE:=’僱員’||P_EMPNO||’工資修改失敗!’;   
  98.          SHOW_MESSAGE;   
  99.          END CHANGE_EMP_SAL;   
  100. —————————- 顯示信息過程 —————————-  
  101.          PROCEDURE SHOW_MESSAGE    
  102.         IS    
  103.         BEGIN  
  104.          DBMS_OUTPUT.PUT_LINE(’提示信息:’||V_MESSAGE);   
  105.         END SHOW_MESSAGE;   
  106. ———————— 判斷僱員是否存在函數 ——————-  
  107.          FUNCTION EXIST_EMP(P_EMPNO NUMBER)   
  108.          RETURN BOOLEAN    
  109.          IS  
  110.         V_NUM NUMBER; –局部變量  
  111.         BEGIN  
  112.         SELECT COUNT(*) INTO V_NUM FROM EMP WHERE EMPNO=P_EMPNO;   
  113. IF V_NUM=1 THEN    
  114.            RETURN TRUE;   
  115.          ELSE  
  116.          RETURN FALSE;   
  117.         END IF;    
  118.         END EXIST_EMP;   
  119.         —————————–  
  120.         END EMP_PK;  

  1. CREATE OR REPLACE PACKAGE EMP_PK   
  2.         –包頭部分   
  3.         IS  
  4.         V_EMP_COUNT NUMBER(5);                
  5.         –僱員人數  
  6.         PROCEDURE INIT(P_MAX NUMBER,P_MIN NUMBER);  –初始化  
  7.         PROCEDURE LIST_EMP;                       
  8.         –顯示僱員列表  
  9. PROCEDURE INSERT_EMP(P_EMPNO        NUMBER,P_ENAMEVARCHAR2,P_JOB VARCHAR2,  
  10.         P_SAL NUMBER);                        
  11.         –插入僱員  
  12.         PROCEDURE DELETE_EMP(P_EMPNO NUMBER);       –刪除僱員  
  13.          PROCEDURE CHANGE_EMP_SAL(P_EMPNO NUMBER,P_SAL NUMBER);   
  14.         –修改僱員工資  
  15.         END EMP_PK;  
  16.         /CREATE OR REPLACE PACKAGE BODY EMP_PK  
  17.          –包體部分   
  18.         IS  
  19.         V_MESSAGE VARCHAR2(50); –顯示信息  
  20. V_MAX_SAL NUMBER(7); –工資上限  
  21.         V_MIN_SAL NUMBER(7); –工資下限  
  22.         FUNCTION EXIST_EMP(P_EMPNO NUMBER)  RETURN  BOOLEAN; –判斷僱員是否存在函數  
  23.         PROCEDURE SHOW_MESSAGE; –顯示信息過程  
  24.         ——————————- 初始化過程 —————————-  
  25.         PROCEDURE INIT(P_MAX NUMBER,P_MIN NUMBER)   
  26.         IS   
  27.         BEGIN  
  28.          SELECT COUNT(*) INTO V_EMP_COUNT FROM EMP;  
  29. V_MAX_SAL:=P_MAX;  
  30.          V_MIN_SAL:=P_MIN;  
  31.          V_MESSAGE:=’初始化過程已經完成!’;  
  32.          SHOW_MESSAGE;   
  33.         END INIT;  
  34. —————————- 顯示僱員列表過程 ———————  
  35.         PROCEDURE LIST_EMP   
  36.          IS   
  37.         BEGIN  
  38. DBMS_OUTPUT.PUT_LINE(’姓名       職務      工資’);  
  39.         FOR emp_rec IN (SELECT * FROM EMP)  
  40.         LOOP  
  41.     DBMS_OUTPUT.PUT_LINE(RPAD(emp_rec.ename,10,)||RPAD(emp_rec.job,10,‘ ’)||TO_CHAR(emp_rec.sal));  
  42.          END LOOP;  
  43.          DBMS_OUTPUT.PUT_LINE(’僱員總人數’||V_EMP_COUNT);  
  44.         END LIST_EMP;  
  45. —————————– 插入僱員過程 —————————–  
  46.         PROCEDUREINSERT_EMP(P_EMPNO     NUMBER,P_ENAMEVARCHAR2,P_JOB    VARCHAR2,P_SAL NUMBER)  
  47.          IS   
  48.         BEGIN  
  49.         IF NOT EXIST_EMP(P_EMPNO) THEN  
  50.         INSERT INTO EMP(EMPNO,ENAME,JOB,SAL)        VALUES(P_EMPNO,P_ENAME,P_JOB,P_SAL);  
  51.         COMMIT;   
  52.         V_EMP_COUNT:=V_EMP_COUNT+1;  
  53.         V_MESSAGE:=’僱員’||P_EMPNO||‘已插入!’;  
  54.         ELSE  
  55. V_MESSAGE:=’僱員’||P_EMPNO||‘已存在,不能插入!’;  
  56.       END IF;  
  57.      SHOW_MESSAGE;   
  58.      EXCEPTION  
  59.     WHEN OTHERS THEN  
  60.      V_MESSAGE:=’僱員’||P_EMPNO||‘插入失敗!’;  
  61.      SHOW_MESSAGE;  
  62.      END INSERT_EMP;  
  63. ————————— 刪除僱員過程 ——————–  
  64.          PROCEDURE DELETE_EMP(P_EMPNO NUMBER)   
  65.         IS   
  66.         BEGIN   
  67.         IF EXIST_EMP(P_EMPNO) THEN  
  68.         DELETE FROM EMP WHERE EMPNO=P_EMPNO;  
  69.         COMMIT;  
  70.          V_EMP_COUNT:=V_EMP_COUNT-1;  
  71.          V_MESSAGE:=’僱員’||P_EMPNO||‘已刪除!’;  
  72.          ELSE  
  73. V_MESSAGE:=’僱員’||P_EMPNO||‘不存在,不能刪除!’;  
  74.     END IF;  
  75.     SHOW_MESSAGE;  
  76.      EXCEPTION  
  77.      WHEN OTHERS THEN  
  78.      V_MESSAGE:=’僱員’||P_EMPNO||‘刪除失敗!’;  
  79.      SHOW_MESSAGE;  
  80.     END DELETE_EMP;  
  81. ————————————— 修改僱員工資過程 ————————————  
  82.         PROCEDURE CHANGE_EMP_SAL(P_EMPNO NUMBER,P_SAL NUMBER)   
  83.          IS   
  84.          BEGIN   
  85.          IF (P_SAL>V_MAX_SAL OR P_SAL<V_MIN_SAL) THEN  
  86.          V_MESSAGE:=’工資超出修改範圍!’;  
  87.         ELSIF NOT EXIST_EMP(P_EMPNO) THEN  
  88.         V_MESSAGE:=’僱員’||P_EMPNO||‘不存在,不能修改工資!’;  
  89. ELSE  
  90.          UPDATE EMP SET SAL=P_SAL WHERE EMPNO=P_EMPNO;  
  91.         COMMIT;  
  92.         V_MESSAGE:=’僱員’||P_EMPNO||‘工資已經修改!’;  
  93.         END IF;  
  94.         SHOW_MESSAGE;  
  95.         EXCEPTION  
  96.          WHEN OTHERS THEN  
  97.          V_MESSAGE:=’僱員’||P_EMPNO||‘工資修改失敗!’;  
  98.          SHOW_MESSAGE;  
  99.          END CHANGE_EMP_SAL;  
  100. —————————- 顯示信息過程 —————————-  
  101.          PROCEDURE SHOW_MESSAGE   
  102.         IS   
  103.         BEGIN  
  104.          DBMS_OUTPUT.PUT_LINE(’提示信息:’||V_MESSAGE);  
  105.         END SHOW_MESSAGE;  
  106. ———————— 判斷僱員是否存在函數 ——————-  
  107.          FUNCTION EXIST_EMP(P_EMPNO NUMBER)  
  108.          RETURN BOOLEAN   
  109.          IS  
  110.         V_NUM NUMBER; –局部變量  
  111.         BEGIN  
  112.         SELECT COUNT(*) INTO V_NUM FROM EMP WHERE EMPNO=P_EMPNO;  
  113. IF V_NUM=1 THEN   
  114.            RETURN TRUE;  
  115.          ELSE  
  116.          RETURN FALSE;  
  117.         END IF;   
  118.         END EXIST_EMP;  
  119.         —————————–  
  120.         END EMP_PK;  


結果爲:
Sql代碼 複製代碼
  1. 程序包已創建。   
  2.         程序包主體已創建。  

  1. 程序包已創建。  
  2.         程序包主體已創建。  



步驟2:初始化包:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2. EXECUTE EMP_PK.INIT(6000,600);  

  1. SET SERVEROUTPUT ON  
  2. EXECUTE EMP_PK.INIT(6000,600);  



顯示爲:
Sql代碼 複製代碼
  1. 提示信息:初始化過程已經完成!  

  1. 提示信息:初始化過程已經完成!  



步驟3:顯示僱員列表:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.LIST_EMP;  

  1. EXECUTE EMP_PK.LIST_EMP;  



顯示爲:
Sql代碼 複製代碼
  1. 姓名          職務          工資   
  2.         SMITH       CLERK       1560   
  3.         ALLEN       SALESMAN    1936   
  4.         WARD        SALESMAN    1830   
  5.         JONES       MANAGER     2975   
  6.         …   
  7.         僱員總人數:14   
  8.         PL/SQL 過程已成功完成。  

  1. 姓名          職務          工資  
  2.         SMITH       CLERK       1560  
  3.         ALLEN       SALESMAN    1936  
  4.         WARD        SALESMAN    1830  
  5.         JONES       MANAGER     2975  
  6.         …  
  7.         僱員總人數:14  
  8.         PL/SQL 過程已成功完成。  



步驟4:插入一個新記錄:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.INSERT_EMP(8001,’小王’,’CLERK’,1000);  

  1. EXECUTE EMP_PK.INSERT_EMP(8001,‘小王’,‘CLERK’,1000);  



顯示結果爲:
Sql代碼 複製代碼
  1. 提示信息:僱員8001已插入!   
  2. PL/SQL 過程已成功完成。  

  1. 提示信息:僱員8001已插入!  
  2. PL/SQL 過程已成功完成。  



步驟5:通過全局變量V_EMP_COUNT查看僱員人數:
Sql代碼 複製代碼
  1. BEGIN  
  2. DBMS_OUTPUT.PUT_LINE(EMP_PK.V_EMP_COUNT);   
  3. END;  

  1. BEGIN  
  2. DBMS_OUTPUT.PUT_LINE(EMP_PK.V_EMP_COUNT);  
  3. END;  



顯示結果爲:
Sql代碼 複製代碼
  1. 15   
  2. PL/SQL 過程已成功完成。  

  1. 15  
  2. PL/SQL 過程已成功完成。  



步驟6:刪除新插入記錄:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.DELETE_EMP(8001);  

  1. EXECUTE EMP_PK.DELETE_EMP(8001);  



顯示結果爲:
Sql代碼 複製代碼
  1. 提示信息:僱員8001已刪除!   
  2.         PL/SQL 過程已成功完成。  

  1. 提示信息:僱員8001已刪除!  
  2.         PL/SQL 過程已成功完成。  



再次刪除該僱員:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.DELETE_EMP(8001);  

  1. EXECUTE EMP_PK.DELETE_EMP(8001);  



結果爲:
Sql代碼 複製代碼
  1. 提示信息:僱員8001不存在,不能刪除!  

  1. 提示信息:僱員8001不存在,不能刪除!  



步驟7:修改僱員工資:
Sql代碼 複製代碼
  1. EXECUTE EMP_PK.CHANGE_EMP_SAL(7788,8000);  

  1. EXECUTE EMP_PK.CHANGE_EMP_SAL(7788,8000);  



顯示結果爲:
Sql代碼 複製代碼
  1. 提示信息:工資超出修改範圍!   
  2.         PL/SQL 過程已成功完成。  

  1. 提示信息:工資超出修改範圍!  
  2.         PL/SQL 過程已成功完成。  



步驟8:授權其他用戶調用包:
如果是另外一個用戶要使用該包,必須由包的所有者授權,下面授予STUDEN賬戶對該包的使用權:
Sql代碼 複製代碼
  1. GRANT EXECUTE ON EMP_PK TO STUDENT;  

  1. GRANT EXECUTE ON EMP_PK TO STUDENT;  



每一個新的會話要爲包中的公用變量開闢新的存儲空間,所以需要重新執行初始化過程。兩個會話的進程互不影響。
步驟9:其他用戶調用包。
啓動另外一個SQL*Plus,登錄STUDENT賬戶,執行以下過程:
Sql代碼 複製代碼
  1. SET SERVEROUTPUT ON  
  2.         EXECUTE SCOTT.EMP_PK. EMP_PK.INIT(5000,700);  

  1. SET SERVEROUTPUT ON  
  2.         EXECUTE SCOTT.EMP_PK. EMP_PK.INIT(5000,700);  



結果爲:
Sql代碼 複製代碼
  1. 提示信息:初始化過程已經完成!   
  2.         PL/SQL 過程已成功完成。  

  1. 提示信息:初始化過程已經完成!  
  2.         PL/SQL 過程已成功完成。  



說明:在初始化中設置僱員的總人數和修改工資的上、下限,初始化後V_EMP_COUNT爲14人,插入僱員後V_EMP_COUNT爲15人。V_EMP_COUNT爲公有變量,所以可以在外部程序中使用DBMS_OUTPUT.PUT_LINE輸出,引用時用EMP_PK.V_EMP_COUNT的形式,說明所屬的包。而私有變量V_MAX_SAL和V_MIN_SAL不能被外部訪問,只能通過內部過程來修改。同樣,EXIST_EMP和SHOW_MESSAGE也是私有過程,也只能在過程體內被其他模塊引用。
注意:在最後一個步驟中,因爲STUDENT模式調用了SCOTT模式的包,所以包名前要增加模式名SCOTT。不同的會話對包的調用屬於不同的應用,所以需要重新進行初始化。
練習
1.如果存儲過程的參數類型爲OUT,那麼調用時傳遞的參數應該爲:
     A.常量 B.表達式                C.變量 D.都可以
2.下列有關存儲過程的特點說法錯誤的是:
     A.存儲過程不能將值傳回調用的主程序
     B.存儲過程是一個命名的模塊
     C.編譯的存儲過程存放在數據庫中
     D.一個存儲過程可以調用另一個存儲過程
3.下列有關函數的特點說法錯誤的是:
     A.函數必須定義返回類型
     B.函數參數的類型只能是IN
     C.在函數體內可以多次使用RETURN語句
     D.函數的調用應使用EXECUTE命令
4.包中不能包含的元素爲:
     A.存儲過程 B.存儲函數
     C.遊標    D.表
5.下列有關包的使用說法錯誤的是:
     A.在不同的包內模塊可以重名
     B.包的私有過程不能被外部程序調用
     C.包體中的過程和函數必須在包頭部分說明
     D.必須先創建包頭,然後創建包體
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章