1. 程式人生 > >oracle儲存過程 真-詳解

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. CREATEORREPLACEPROCEDURE EMP_COUNT   
  2. AS
  3. V_TOTAL NUMBER(10);   
  4. BEGIN
  5. SELECTCOUNT(*) INTO V_TOTAL FROM EMP;   
  6.  DBMS_OUTPUT.PUT_LINE('僱員總人數為:'||V_TOTAL);   
  7. END;  
  1. CREATEORREPLACEPROCEDURE EMP_COUNT  
  2. AS
  3. V_TOTAL NUMBER(10);  
  4. BEGIN
  5.  SELECTCOUNT(*) 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. GRANTEXECUTEON EMP_COUNT TO STUDENT  
  1. GRANTEXECUTEON 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. CREATEORREPLACEPROCEDURE 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. CREATEORREPLACEPROCEDURE 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. INOUT  定義一個輸入、輸出引數變數,兼有以上兩者的功能  
  1. IN  定義一個輸入引數變數,用於傳遞引數給儲存過程  
  2. OUT 定義一個輸出引數變數,用於從儲存過程獲取資料  
  3. INOUT  定義一個輸入、輸出引數變數,兼有以上兩者的功能  

引數的定義形式和作用如下:
引數名 IN 資料型別 DEFAULT 值;
定義一個輸入引數變數,用於傳遞引數給儲存過程。在呼叫儲存過程時,主程式的實際引數可以是常量、有值變數或表示式等。DEFAULT 關鍵字為可選項,用來設定引數的預設值。如果在呼叫儲存過程時不指明引數,則引數變數取預設值。在儲存過程中,輸入變數接收主程式傳遞的值,但不能對其進行賦值。
引數名 OUT 資料型別;
定義一個輸出引數變數,用於從儲存過程獲取資料,即變數從儲存過程中返回值給主程式。
在呼叫儲存過程時,主程式的實際引數只能是一個變數,而不能是常量或表示式。在儲存過程中,引數變數只能被賦值而不能將其用於賦值,在儲存過程中必須給輸出變數至少賦值一次。
引數名 IN OUT 資料型別 DEFAULT 值;
定義一個輸入、輸出引數變數,兼有以上兩者的功能。在呼叫儲存過程時,主程式的實際引數只能是一個變數,而不能是常量或表示式。DEFAULT 關鍵字為可選項,用來設定引數的預設值。在儲存過程中,變數接收主程式傳遞的值,同時可以參加賦值運算,也可以對其進行賦值。在儲存過程中必須給變數至少賦值一次。
如果省略IN、OUT或IN OUT,則預設模式是IN。
【訓練1】  編寫給僱員增加工資的儲存過程CHANGE_SALARY,通過IN型別的引數傳遞要增加工資的僱員編號和增加的工資額。
步驟1:登入SCOTT賬戶。
  步驟2:在SQL*Plus輸入區中輸入以下儲存過程並執行:
Sql程式碼 複製程式碼
  1. CREATEORREPLACEPROCEDURE 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. CREATEORREPLACEPROCEDURE 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. CREATEORREPLACEPROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)   
  2. AS
  3. BEGIN
  4. SELECTCOUNT(*) INTO P_TOTAL FROM EMP;   
  5. END;  
  1. CREATEORREPLACEPROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)  
  2.         AS
  3.         BEGIN
  4.         SELECTCOUNT(*) 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. CREATEORREPLACEPROCEDURE ADD_REGION(P_HPONE_NUM INOUT VARCHAR2)   
  2. AS
  3. BEGIN
  4.          P_HPONE_NUM:='0755-'||P_HPONE_NUM;   
  5. END;  
  1. CREATEORREPLACEPROCEDURE ADD_REGION(P_HPONE_NUM INOUT 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. CREATEORREPLACEFUNCTION 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. CREATEORREPLACEFUNCTION 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 WHERENAME='EMP_COUNT';  
  1. select TEXT  from user_source WHERENAME='EMP_COUNT';  

結果為:
Sql程式碼 複製程式碼
  1. TEXT   
  2. --------------------------------------------------------------------------------
  3. PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)   
  4. AS
  5. BEGIN
  6. SELECTCOUNT(*) INTO P_TOTAL FROM EMP;   
  7. END;  
  1. TEXT  
  2. --------------------------------------------------------------------------------
  3. PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)  
  4. AS
  5. BEGIN
  6.  SELECTCOUNT(*) INTO P_TOTAL FROM EMP;