日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

oracle存储过程实例

發布時間:2023/12/14 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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代碼
  • CREATE?OR?REPLACE?PROCEDURE?EMP_COUNT ??
  • AS??
  • V_TOTAL?NUMBER(10); ??
  • BEGIN??
  • ?SELECT?COUNT(*)?INTO?V_TOTAL?FROM?EMP; ??
  • ?DBMS_OUTPUT.PUT_LINE('雇員總人數為:'||V_TOTAL); ??
  • END;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?PROCEDURE?EMP_COUNT??
  • AS??
  • V_TOTAL?NUMBER(10);??
  • BEGIN??
  • ?SELECT?COUNT(*)?INTO?V_TOTAL?FROM?EMP;??
  • ?DBMS_OUTPUT.PUT_LINE('雇員總人數為:'||V_TOTAL);??
  • END;??

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

  • 步驟4:調用存儲過程,在輸入區中輸入以下語句并執行:
    Sql代碼
  • EXECUTE?EMP_COUNT;??
  • [sql] view plaincopy
  • EXECUTE?EMP_COUNT;??

  • 顯示結果為:
    Sql代碼
  • 雇員總人數為:14 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 雇員總人數為:14??
  • ????????PL/SQL?過程已成功完成。??

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

  • Sql代碼
  • 授權成功。??
  • [sql] view plaincopy
  • 授權成功。??

  • 步驟3:登錄STUDENT賬戶,在SQL*Plus輸入區中輸入以下程序:
    Sql代碼
  • SET?SERVEROUTPUT?ON??
  • ????????BEGIN??
  • ????????SCOTT.EMP_COUNT; ??
  • ????????END;??
  • [sql] view plaincopy
  • SET?SERVEROUTPUT?ON??
  • ????????BEGIN??
  • ????????SCOTT.EMP_COUNT;??
  • ????????END;??

  • 步驟4:執行以上程序,結果為:
    Sql代碼
  • 雇員總人數為:14 ??
  • ????????PL/SQL?過程已成功完成。???
  • [sql] view plaincopy
  • 雇員總人數為:14??
  • ????????PL/SQL?過程已成功完成。???

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

  • 執行結果:
    Sql代碼
  • 過程已創建。??
  • [sql] view plaincopy
  • 過程已創建。??

  • 步驟2:調用存儲過程,在輸入區中輸入以下語句并執行:
    Sql代碼
  • EXECUTE?EMP_LIST??
  • [sql] view plaincopy
  • EXECUTE?EMP_LIST??

  • 顯示結果為:
    Sql代碼
  • 7369SMITH ??
  • 7499ALLEN ??
  • 7521WARD ??
  • 7566JONES ??
  • ????????????執行結果: ??
  • ????????雇員總人數為:14 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 7369SMITH??
  • 7499ALLEN??
  • 7521WARD??
  • 7566JONES??
  • ????????????執行結果:??
  • ????????雇員總人數為:14??
  • ????????PL/SQL?過程已成功完成。??

  • 說明:以上的EMP_LIST存儲過程中定義并使用了游標,用來循環顯示所有雇員的信息。然后調用已經成功編譯的存儲過程EMP_COUNT,用來附加顯示雇員總人數。通過EXECUTE命令來執行EMP_LIST存儲過程。
    【練習1】編寫顯示部門信息的存儲過程DEPT_LIST,要求統計出部門個數。
    參數傳遞
    參數的作用是向存儲過程傳遞數據,或從存儲過程獲得返回結果。正確的使用參數可以大大增加存儲過程的靈活性和通用性。
    參數的類型有三種,如下所示。
    Sql代碼
  • IN??定義一個輸入參數變量,用于傳遞參數給存儲過程 ??
  • OUT?定義一個輸出參數變量,用于從存儲過程獲取數據 ??
  • IN?OUT??定義一個輸入、輸出參數變量,兼有以上兩者的功能??
  • [sql] view plaincopy
  • IN??定義一個輸入參數變量,用于傳遞參數給存儲過程??
  • OUT?定義一個輸出參數變量,用于從存儲過程獲取數據??
  • 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代碼
  • CREATE?OR?REPLACE?PROCEDURE?CHANGE_SALARY(P_EMPNO?IN?NUMBER?DEFAULT?7788,P_RAISE?NUMBER?DEFAULT?10) ??
  • ????????AS??
  • ?????????V_ENAME?VARCHAR2(10); ??
  • V_SAL?NUMBER(5); ??
  • ????????BEGIN??
  • ????????SELECT?ENAME,SAL?INTO?V_ENAME,V_SAL?FROM?EMP?WHERE?EMPNO=P_EMPNO; ??
  • ?????????UPDATE?EMP?SET?SAL=SAL+P_RAISE?WHERE?EMPNO=P_EMPNO; ??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員'||V_ENAME||'的工資被改為'||TO_CHAR(V_SAL+P_RAISE)); ??
  • COMMIT; ??
  • ????????EXCEPTION ??
  • ?????????WHEN?OTHERS?THEN??
  • ????????DBMS_OUTPUT.PUT_LINE('發生錯誤,修改失敗!'); ??
  • ????????ROLLBACK; ??
  • ????????END;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?PROCEDURE?CHANGE_SALARY(P_EMPNO?IN?NUMBER?DEFAULT?7788,P_RAISE?NUMBER?DEFAULT?10)??
  • ????????AS??
  • ?????????V_ENAME?VARCHAR2(10);??
  • V_SAL?NUMBER(5);??
  • ????????BEGIN??
  • ????????SELECT?ENAME,SAL?INTO?V_ENAME,V_SAL?FROM?EMP?WHERE?EMPNO=P_EMPNO;??
  • ?????????UPDATE?EMP?SET?SAL=SAL+P_RAISE?WHERE?EMPNO=P_EMPNO;??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員'||V_ENAME||'的工資被改為'||TO_CHAR(V_SAL+P_RAISE));??
  • COMMIT;??
  • ????????EXCEPTION??
  • ?????????WHEN?OTHERS?THEN??
  • ????????DBMS_OUTPUT.PUT_LINE('發生錯誤,修改失敗!');??
  • ????????ROLLBACK;??
  • ????????END;??

  • 執行結果為:
    Sql代碼
  • 過程已創建。??
  • [sql] view plaincopy
  • 過程已創建。??

  • 步驟3:調用存儲過程,在輸入區中輸入以下語句并執行:
    Sql代碼
  • EXECUTE?CHANGE_SALARY(7788,80)??
  • [sql] view plaincopy
  • EXECUTE?CHANGE_SALARY(7788,80)??

  • 顯示結果為:
    Sql代碼
  • 雇員SCOTT的工資被改為3080???
  • [sql] view plaincopy
  • 雇員SCOTT的工資被改為3080???

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

  • 顯示結果為:
    Sql代碼
  • 雇員SCOTT的工資被改為3090???
  • [sql] view plaincopy
  • 雇員SCOTT的工資被改為3090???

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

  • 執行結果為:
    Sql代碼
  • 過程已創建。??
  • [sql] view plaincopy
  • 過程已創建。??

  • 步驟3:輸入以下程序并執行:
    Sql代碼
  • DECLARE??
  • ????????V_EMPCOUNT?NUMBER; ??
  • ????????BEGIN??
  • ????????EMP_COUNT(V_EMPCOUNT); ??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員總人數為:'||V_EMPCOUNT); ??
  • ????????END;??
  • [sql] view plaincopy
  • DECLARE??
  • ????????V_EMPCOUNT?NUMBER;??
  • ????????BEGIN??
  • ????????EMP_COUNT(V_EMPCOUNT);??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員總人數為:'||V_EMPCOUNT);??
  • ????????END;??

  • 顯示結果為:
    Sql代碼
  • 雇員總人數為:14 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 雇員總人數為:14??
  • ????????PL/SQL?過程已成功完成。??

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

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

  • 執行結果為:
    Sql代碼
  • 過程已創建。??
  • [sql] view plaincopy
  • 過程已創建。??

  • 步驟3:輸入以下程序并執行:
    Sql代碼
  • SET?SERVEROUTPUT?ON??
  • DECLARE??
  • V_PHONE_NUM?VARCHAR2(15); ??
  • BEGIN??
  • V_PHONE_NUM:='26731092'; ??
  • ADD_REGION(V_PHONE_NUM); ??
  • DBMS_OUTPUT.PUT_LINE('新的電話號碼:'||V_PHONE_NUM); ??
  • END;??
  • [sql] view plaincopy
  • SET?SERVEROUTPUT?ON??
  • DECLARE??
  • V_PHONE_NUM?VARCHAR2(15);??
  • BEGIN??
  • V_PHONE_NUM:='26731092';??
  • ADD_REGION(V_PHONE_NUM);??
  • DBMS_OUTPUT.PUT_LINE('新的電話號碼:'||V_PHONE_NUM);??
  • END;??

  • 顯示結果為:
    Sql代碼
  • 新的電話號碼:0755-26731092 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 新的電話號碼:0755-26731092??
  • ????????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代碼
  • CREATE?OR?REPLACE?FUNCTION?GET_EMP_NAME(P_EMPNO?NUMBER?DEFAULT?7788) ??
  • ????????RETURN?VARCHAR2 ??
  • ????????AS??
  • ?????????V_ENAME?VARCHAR2(10); ??
  • ????????BEGIN??
  • ????????ELECT?ENAME?INTO?V_ENAME?FROM?EMP?WHERE?EMPNO=P_EMPNO; ??
  • RETURN(V_ENAME); ??
  • EXCEPTION ??
  • ?WHEN?NO_DATA_FOUND?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('沒有該編號雇員!'); ??
  • ??RETURN?(NULL); ??
  • ?WHEN?TOO_MANY_ROWS?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('有重復雇員編號!'); ??
  • ??RETURN?(NULL); ??
  • ?WHEN?OTHERS?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('發生其他錯誤!'); ??
  • ??RETURN?(NULL); ??
  • END;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?FUNCTION?GET_EMP_NAME(P_EMPNO?NUMBER?DEFAULT?7788)??
  • ????????RETURN?VARCHAR2??
  • ????????AS??
  • ?????????V_ENAME?VARCHAR2(10);??
  • ????????BEGIN??
  • ????????ELECT?ENAME?INTO?V_ENAME?FROM?EMP?WHERE?EMPNO=P_EMPNO;??
  • RETURN(V_ENAME);??
  • EXCEPTION??
  • ?WHEN?NO_DATA_FOUND?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('沒有該編號雇員!');??
  • ??RETURN?(NULL);??
  • ?WHEN?TOO_MANY_ROWS?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('有重復雇員編號!');??
  • ??RETURN?(NULL);??
  • ?WHEN?OTHERS?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('發生其他錯誤!');??
  • ??RETURN?(NULL);??
  • END;??

  • 步驟3:調用該存儲函數,輸入并執行以下程序:
    Sql代碼
  • BEGIN??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員7369的名稱是:'||?GET_EMP_NAME(7369)); ??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員7839的名稱是:'||?GET_EMP_NAME(7839)); ??
  • ????????END;??
  • [sql] view plaincopy
  • BEGIN??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員7369的名稱是:'||?GET_EMP_NAME(7369));??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員7839的名稱是:'||?GET_EMP_NAME(7839));??
  • ????????END;??

  • 顯示結果為:
    Sql代碼
  • 雇員7369的名稱是:SMITH ??
  • ????????雇員7839的名稱是:KING ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 雇員7369的名稱是:SMITH??
  • ????????雇員7839的名稱是:KING??
  • ????????PL/SQL?過程已成功完成。??

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

  • 結果為:
    Sql代碼
  • 名稱??????????????????????????????????????是否為空??類型 ??
  • ????????-------------------------------------------------------------?-------------?-----------------------??
  • ?NAME???????????????????????????????????????????????VARCHAR2(30) ??
  • ?TYPE???????????????????????????????????????????????VARCHAR2(12) ??
  • ?LINE???????????????????????????????????????????????NUMBER ??
  • ?TEXT???????????????????????????????????????????????VARCHAR2(4000)??
  • [sql] view plaincopy
  • 名稱??????????????????????????????????????是否為空??類型??
  • ????????-------------------------------------------------------------?-------------?-----------------------??
  • ?NAME???????????????????????????????????????????????VARCHAR2(30)??
  • ?TYPE???????????????????????????????????????????????VARCHAR2(12)??
  • ?LINE???????????????????????????????????????????????NUMBER??
  • ?TEXT???????????????????????????????????????????????VARCHAR2(4000)??

  • ? 說明:里面按行存放著過程或函數的腳本,NAME是過程或函數名,TYPE 代表類型(PROCEDURE或FUNCTION),LINE是行號,TEXT 為腳本。
    【訓練1】? 查詢過程EMP_COUNT的腳本。
    在SQL*Plus中輸入并執行如下查詢:
    Sql代碼
  • select?TEXT??from?user_source?WHERE?NAME='EMP_COUNT';??
  • [sql] view plaincopy
  • select?TEXT??from?user_source?WHERE?NAME='EMP_COUNT';??

  • 結果為:
    Sql代碼
  • TEXT ??
  • --------------------------------------------------------------------------------??
  • PROCEDURE?EMP_COUNT(P_TOTAL?OUT?NUMBER) ??
  • AS??
  • BEGIN??
  • ?SELECT?COUNT(*)?INTO?P_TOTAL?FROM?EMP; ??
  • END;??
  • [sql] view plaincopy
  • TEXT??
  • --------------------------------------------------------------------------------??
  • PROCEDURE?EMP_COUNT(P_TOTAL?OUT?NUMBER)??
  • AS??
  • BEGIN??
  • ?SELECT?COUNT(*)?INTO?P_TOTAL?FROM?EMP;??
  • END;??

  • 【訓練2】? 查詢過程GET_EMP_NAME的參數。
    在SQL*Plus中輸入并執行如下查詢:
    Sql代碼
  • DESCRIBE?GET_EMP_NAME??
  • [sql] view plaincopy
  • DESCRIBE?GET_EMP_NAME??

  • 結果為:
    Sql代碼
  • FUNCTION?GET_EMP_NAME?RETURNS?VARCHAR2 ??
  • ????????參數名稱????????????類型??????????輸入/輸出默認值? ??
  • ????????-----------------------------------------?-----------------------------------?-----------------?-------------??
  • ????????P_EMPNO?????????????NUMBER(4)?IN?????DEFAULT??
  • [sql] view plaincopy
  • FUNCTION?GET_EMP_NAME?RETURNS?VARCHAR2??
  • ????????參數名稱????????????類型??????????輸入/輸出默認值???
  • ????????-----------------------------------------?-----------------------------------?-----------------?-------------??
  • ????????P_EMPNO?????????????NUMBER(4)?IN?????DEFAULT??

  • 【訓練3】? 在發生編譯錯誤時,顯示錯誤。
    Sql代碼
  • SHOW?ERRORS??
  • [sql] view plaincopy
  • SHOW?ERRORS??

  • 以下是一段編譯錯誤顯示:
    Sql代碼
  • LINE/COL?ERROR ??
  • ????????-------------?-----------------------------------------------------------------??
  • ????????4/2???????PL/SQL:?SQL?Statement?ignored ??
  • ????????4/36??????PLS-00201:?必須說明標識符?'EMPP'??
  • [sql] view plaincopy
  • LINE/COL?ERROR??
  • ????????-------------?-----------------------------------------------------------------??
  • ????????4/2???????PL/SQL:?SQL?Statement?ignored??
  • ????????4/36??????PLS-00201:?必須說明標識符?'EMPP'??

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

  • 結果為:
    Sql代碼
  • STATUS ??
  • ????????------------??
  • ????????VALID??
  • [sql] view plaincopy
  • STATUS??
  • ????????------------??
  • ????????VALID??

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

  • 結果:
    Sql代碼
  • 名稱?????????????????????是否為空??類型 ??
  • ????????--------------------------------------------------------------?-------------?----------------------------??
  • ?????????NAME????????????NOT?NULL???VARCHAR2(30) ??
  • ?????????TYPE???????????????????????VARCHAR2(12) ??
  • ????????REFERENCED_OWNER????????????????????????????????VARCHAR2(30) ??
  • ?REFERENCED_NAME????????????????????????????????VARCHAR2(64) ??
  • ?REFERENCED_TYPE????????????????????????????????VARCHAR2(12) ??
  • REFERENCED_LINK_NAME????????????????????????????VARCHAR2(128) ??
  • ????????SCHEMAID????????????????????????????????????????NUMBER ??
  • ?????????DEPENDENCY_TYPE????????????????????????????????VARCHAR2(4)??
  • [sql] view plaincopy
  • 名稱?????????????????????是否為空??類型??
  • ????????--------------------------------------------------------------?-------------?----------------------------??
  • ?????????NAME????????????NOT?NULL???VARCHAR2(30)??
  • ?????????TYPE???????????????????????VARCHAR2(12)??
  • ????????REFERENCED_OWNER????????????????????????????????VARCHAR2(30)??
  • ?REFERENCED_NAME????????????????????????????????VARCHAR2(64)??
  • ?REFERENCED_TYPE????????????????????????????????VARCHAR2(12)??
  • REFERENCED_LINK_NAME????????????????????????????VARCHAR2(128)??
  • ????????SCHEMAID????????????????????????????????????????NUMBER??
  • ?????????DEPENDENCY_TYPE????????????????????????????????VARCHAR2(4)??

  • ? 說明:NAME為實體名,TYPE為實體類型,REFERENCED_OWNER為涉及到的實體擁有者賬戶,REFERENCED_NAME為涉及到的實體名,REFERENCED_TYPE 為涉及到的實體類型。
    【訓練5】? 查詢EMP_LIST存儲過程的依賴性。
    Sql代碼
  • SELECT?REFERENCED_NAME,REFERENCED_TYPE?FROM?USER_DEPENDENCIES?WHERE?NAME='EMP_LIST';??
  • [sql] view plaincopy
  • SELECT?REFERENCED_NAME,REFERENCED_TYPE?FROM?USER_DEPENDENCIES?WHERE?NAME='EMP_LIST';??

  • 執行結果:
    Sql代碼
  • REFERENCED_NAME?????????????????????????????????????????REFERENCED_TYPE ??
  • ????????------------------------------------------------------------------------------------------?----------------------------??
  • STANDARD????????????????????????????????????????????????PACKAGE ??
  • ????????SYS_STUB_FOR_PURITY_ANALYSIS????????????????????????????PACKAGE ??
  • ????????DBMS_OUTPUT?????????????????????????????????????????????????PACKAGE ??
  • ????????DBMS_OUTPUT?????????????????????????????????????????????SYNONYM ??
  • DBMS_OUTPUT??????????????????????NON-EXISTENT ??
  • ????????EMP?????????????????????????????????????????????????????????TABLE??
  • ????????EMP_COUNT???????????????????????????????????????????????????PROCEDURE??
  • [sql] view plaincopy
  • REFERENCED_NAME?????????????????????????????????????????REFERENCED_TYPE??
  • ????????------------------------------------------------------------------------------------------?----------------------------??
  • STANDARD????????????????????????????????????????????????PACKAGE??
  • ????????SYS_STUB_FOR_PURITY_ANALYSIS????????????????????????????PACKAGE??
  • ????????DBMS_OUTPUT?????????????????????????????????????????????????PACKAGE??
  • ????????DBMS_OUTPUT?????????????????????????????????????????????SYNONYM??
  • DBMS_OUTPUT??????????????????????NON-EXISTENT??
  • ????????EMP?????????????????????????????????????????????????????????TABLE??
  • ????????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代碼
  • 過程(PROCUDURE)???帶參數的命名的程序模塊 ??
  • 函數(FUNCTION)????帶參數、具有返回值的命名的程序模塊 ??
  • 變量(VARIABLE)????存儲變化的量的存儲單元 ??
  • 常量(CONSTANT)????存儲不變的量的存儲單元 ??
  • 游標(CURSOR)??用戶定義的數據操作緩存區,在可執行部分使用 ??
  • 類型(TYPE)????用戶定義的新的結構類型 ??
  • 異常(EXCEPTION)???在標準包中定義或由用戶自定義,用于處理程序錯誤??
  • [sql] view plaincopy
  • 過程(PROCUDURE)???帶參數的命名的程序模塊??
  • 函數(FUNCTION)????帶參數、具有返回值的命名的程序模塊??
  • 變量(VARIABLE)????存儲變化的量的存儲單元??
  • 常量(CONSTANT)????存儲不變的量的存儲單元??
  • 游標(CURSOR)??用戶定義的數據操作緩存區,在可執行部分使用??
  • 類型(TYPE)????用戶定義的新的結構類型??
  • 異常(EXCEPTION)???在標準包中定義或由用戶自定義,用于處理程序錯誤??

  • 說明部分可以出現在包的三個不同的部分:出現在包頭中的稱為公有元素,出現在包體中的稱為私有元素,出現在包體的過程(或函數)中的稱為局部變量。它們的性質有所不同,如下所示。
    Sql代碼
  • 公有元素(PUBLIC)????在包頭中說明,在包體中具體定義?在包外可見并可以訪問,對整個應用的全過程有效 ??
  • 私有元素(PRIVATE)???在包體的說明部分說明??只能被包內部的其他部分訪問 ??
  • 局部變量(LOCAL)?在過程或函數的說明部分說明???只能在定義變量的過程或函數中使用??
  • [sql] view plaincopy
  • 公有元素(PUBLIC)????在包頭中說明,在包體中具體定義?在包外可見并可以訪問,對整個應用的全過程有效??
  • 私有元素(PRIVATE)???在包體的說明部分說明??只能被包內部的其他部分訪問??
  • 局部變量(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代碼
  • DBMS_OUTPUT?在SQL*Plus環境下輸出信息 ??
  • DBMS_DDL????編譯過程函數和包 ??
  • DBMS_SESSION????改變用戶的會話,初始化包等 ??
  • DBMS_TRANSACTION????控制數據庫事務 ??
  • DBMS_MAIL???連接Oracle*Mail ??
  • DBMS_LOCK???進行復雜的鎖機制管理 ??
  • DBMS_ALERT??識別數據庫事件告警 ??
  • DBMS_PIPE???通過管道在會話間傳遞信息 ??
  • DBMS_JOB????管理Oracle的作業 ??
  • DBMS_LOB????操縱大對象 ??
  • DBMS_SQL????執行動態SQL語句??
  • [sql] view plaincopy
  • DBMS_OUTPUT?在SQL*Plus環境下輸出信息??
  • DBMS_DDL????編譯過程函數和包??
  • DBMS_SESSION????改變用戶的會話,初始化包等??
  • DBMS_TRANSACTION????控制數據庫事務??
  • DBMS_MAIL???連接Oracle*Mail??
  • DBMS_LOCK???進行復雜的鎖機制管理??
  • DBMS_ALERT??識別數據庫事件告警??
  • DBMS_PIPE???通過管道在會話間傳遞信息??
  • DBMS_JOB????管理Oracle的作業??
  • DBMS_LOB????操縱大對象??
  • DBMS_SQL????執行動態SQL語句??

  • 包的應用
    在SQL*Plus環境下,包和包體可以分別編譯,也可以一起編譯。如果分別編譯,則要先編譯包頭,后編譯包體。如果在一起編譯,則包頭寫在前,包體在后,中間用“/”分隔。
    可以將已經存在的存儲過程或函數添加到包中,方法是去掉過程或函數創建語句的CREATE OR REPLACE部分,將存儲過程或函數復制到包體中 ,然后重新編譯即可。
    ?? 如果需要將私有過程或函數變成共有過程或函數的話,將過程或函數說明部分復制到包頭說明部分,然后重新編譯就可以了。
    【訓練1】? 創建管理雇員信息的包EMPLOYE,它具有從EMP表獲得雇員信息,修改雇員名稱,修改雇員工資和寫回EMP表的功能。
    步驟1:登錄SCOTT賬戶,輸入以下代碼并編譯:
    Sql代碼
  • CREATE?OR?REPLACE?PACKAGE?EMPLOYE?--包頭部分???
  • ????????IS??
  • ?PROCEDURE?SHOW_DETAIL;? ??
  • ?PROCEDURE?GET_EMPLOYE(P_EMPNO?NUMBER);? ??
  • ?PROCEDURE?SAVE_EMPLOYE;? ??
  • ?PROCEDURE?CHANGE_NAME(P_NEWNAME?VARCHAR2);? ??
  • PROCEDURE?CHANGE_SAL(P_NEWSAL?NUMBER);? ??
  • ????????END?EMPLOYE; ??
  • ????????/ ??
  • ????????CREATE?OR?REPLACE?PACKAGE?BODY?EMPLOYE?--包體部分???
  • ????????IS??
  • ?EMPLOYE?EMP%ROWTYPE; ??
  • ????????--------------?顯示雇員信息?---------------??
  • ????????PROCEDURE?SHOW_DETAIL ??
  • ????????AS??
  • ????????BEGIN??
  • DBMS_OUTPUT.PUT_LINE(‘-----?雇員信息?-----’);?????
  • ????????DBMS_OUTPUT.PUT_LINE('雇員編號:'||EMPLOYE.EMPNO); ??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員名稱:'||EMPLOYE.ENAME); ??
  • ??????????DBMS_OUTPUT.PUT_LINE('雇員職務:'||EMPLOYE.JOB); ??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員工資:'||EMPLOYE.SAL); ??
  • ?????????DBMS_OUTPUT.PUT_LINE('部門編號:'||EMPLOYE.DEPTNO); ??
  • ????????END?SHOW_DETAIL; ??
  • -----------------?從EMP表取得一個雇員?--------------------??
  • ?????????PROCEDURE?GET_EMPLOYE(P_EMPNO?NUMBER) ??
  • ????????AS??
  • ????????BEGIN??
  • ????????SELECT?*?INTO?EMPLOYE?FROM?EMP?WHERE????EMPNO=P_EMPNO; ??
  • ????????DBMS_OUTPUT.PUT_LINE('獲取雇員'||EMPLOYE.ENAME||'信息成功'); ??
  • ?????????EXCEPTION ??
  • ?????????WHEN?OTHERS?THEN??
  • ???????????DBMS_OUTPUT.PUT_LINE('獲取雇員信息發生錯誤!'); ??
  • ????????END?GET_EMPLOYE; ??
  • ----------------------?保存雇員到EMP表?--------------------------??
  • ????????PROCEDURE?SAVE_EMPLOYE ??
  • ????????AS??
  • ????????BEGIN??
  • ????????UPDATE?EMP?SET?ENAME=EMPLOYE.ENAME,?SAL=EMPLOYE.SAL?WHERE?EMPNO= ??
  • ????EMPLOYE.EMPNO; ??
  • ?????DBMS_OUTPUT.PUT_LINE('雇員信息保存完成!'); ??
  • ????????END?SAVE_EMPLOYE; ??
  • ----------------------------?修改雇員名稱?------------------------------??
  • ????????PROCEDURE?CHANGE_NAME(P_NEWNAME?VARCHAR2) ??
  • ?????????AS??
  • ????????BEGIN??
  • ?????????EMPLOYE.ENAME:=P_NEWNAME; ??
  • ?????????DBMS_OUTPUT.PUT_LINE('修改名稱完成!'); ??
  • ????????END?CHANGE_NAME; ??
  • ----------------------------?修改雇員工資?--------------------------??
  • ????????PROCEDURE?CHANGE_SAL(P_NEWSAL?NUMBER) ??
  • ????????AS??
  • ????????BEGIN??
  • ?????????EMPLOYE.SAL:=P_NEWSAL; ??
  • ?????????DBMS_OUTPUT.PUT_LINE('修改工資完成!'); ??
  • ????????END?CHANGE_SAL; ??
  • ????????END?EMPLOYE;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?PACKAGE?EMPLOYE?--包頭部分???
  • ????????IS??
  • ?PROCEDURE?SHOW_DETAIL;???
  • ?PROCEDURE?GET_EMPLOYE(P_EMPNO?NUMBER);???
  • ?PROCEDURE?SAVE_EMPLOYE;???
  • ?PROCEDURE?CHANGE_NAME(P_NEWNAME?VARCHAR2);???
  • PROCEDURE?CHANGE_SAL(P_NEWSAL?NUMBER);???
  • ????????END?EMPLOYE;??
  • ????????/??
  • ????????CREATE?OR?REPLACE?PACKAGE?BODY?EMPLOYE?--包體部分???
  • ????????IS??
  • ?EMPLOYE?EMP%ROWTYPE;??
  • ????????--------------?顯示雇員信息?---------------??
  • ????????PROCEDURE?SHOW_DETAIL??
  • ????????AS??
  • ????????BEGIN??
  • DBMS_OUTPUT.PUT_LINE(‘-----?雇員信息?-----’);?????
  • ????????DBMS_OUTPUT.PUT_LINE('雇員編號:'||EMPLOYE.EMPNO);??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員名稱:'||EMPLOYE.ENAME);??
  • ??????????DBMS_OUTPUT.PUT_LINE('雇員職務:'||EMPLOYE.JOB);??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員工資:'||EMPLOYE.SAL);??
  • ?????????DBMS_OUTPUT.PUT_LINE('部門編號:'||EMPLOYE.DEPTNO);??
  • ????????END?SHOW_DETAIL;??
  • -----------------?從EMP表取得一個雇員?--------------------??
  • ?????????PROCEDURE?GET_EMPLOYE(P_EMPNO?NUMBER)??
  • ????????AS??
  • ????????BEGIN??
  • ????????SELECT?*?INTO?EMPLOYE?FROM?EMP?WHERE????EMPNO=P_EMPNO;??
  • ????????DBMS_OUTPUT.PUT_LINE('獲取雇員'||EMPLOYE.ENAME||'信息成功');??
  • ?????????EXCEPTION??
  • ?????????WHEN?OTHERS?THEN??
  • ???????????DBMS_OUTPUT.PUT_LINE('獲取雇員信息發生錯誤!');??
  • ????????END?GET_EMPLOYE;??
  • ----------------------?保存雇員到EMP表?--------------------------??
  • ????????PROCEDURE?SAVE_EMPLOYE??
  • ????????AS??
  • ????????BEGIN??
  • ????????UPDATE?EMP?SET?ENAME=EMPLOYE.ENAME,?SAL=EMPLOYE.SAL?WHERE?EMPNO=??
  • ????EMPLOYE.EMPNO;??
  • ?????DBMS_OUTPUT.PUT_LINE('雇員信息保存完成!');??
  • ????????END?SAVE_EMPLOYE;??
  • ----------------------------?修改雇員名稱?------------------------------??
  • ????????PROCEDURE?CHANGE_NAME(P_NEWNAME?VARCHAR2)??
  • ?????????AS??
  • ????????BEGIN??
  • ?????????EMPLOYE.ENAME:=P_NEWNAME;??
  • ?????????DBMS_OUTPUT.PUT_LINE('修改名稱完成!');??
  • ????????END?CHANGE_NAME;??
  • ----------------------------?修改雇員工資?--------------------------??
  • ????????PROCEDURE?CHANGE_SAL(P_NEWSAL?NUMBER)??
  • ????????AS??
  • ????????BEGIN??
  • ?????????EMPLOYE.SAL:=P_NEWSAL;??
  • ?????????DBMS_OUTPUT.PUT_LINE('修改工資完成!');??
  • ????????END?CHANGE_SAL;??
  • ????????END?EMPLOYE;??

  • 步驟2:獲取雇員7788的信息:
    Sql代碼
  • SET?SERVEROUTPUT?ON??
  • ????????EXECUTE?EMPLOYE.GET_EMPLOYE(7788);??
  • [sql] view plaincopy
  • SET?SERVEROUTPUT?ON??
  • ????????EXECUTE?EMPLOYE.GET_EMPLOYE(7788);??

  • 結果為:
    Sql代碼
  • 獲取雇員SCOTT信息成功 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 獲取雇員SCOTT信息成功??
  • ????????PL/SQL?過程已成功完成。??

  • 步驟3:顯示雇員信息:
    Sql代碼
  • EXECUTE?EMPLOYE.SHOW_DETAIL;??
  • [sql] view plaincopy
  • EXECUTE?EMPLOYE.SHOW_DETAIL;??

  • 結果為:
    Sql代碼
  • ------------------?雇員信息?------------------??
  • ????????雇員編號:7788 ??
  • ????????雇員名稱:SCOTT ??
  • ????????雇員職務:ANALYST ??
  • ????????雇員工資:3000 ??
  • ????????部門編號:20 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • ------------------?雇員信息?------------------??
  • ????????雇員編號:7788??
  • ????????雇員名稱:SCOTT??
  • ????????雇員職務:ANALYST??
  • ????????雇員工資:3000??
  • ????????部門編號:20??
  • ????????PL/SQL?過程已成功完成。??

  • 步驟4:修改雇員工資:
    Sql代碼
  • EXECUTE?EMPLOYE.CHANGE_SAL(3800);??
  • [sql] view plaincopy
  • EXECUTE?EMPLOYE.CHANGE_SAL(3800);??

  • 結果為:
    Sql代碼
  • 修改工資完成! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 修改工資完成!??
  • ????????PL/SQL?過程已成功完成。??

  • 步驟5:將修改的雇員信息存入EMP表
    Sql代碼
  • EXECUTE?EMPLOYE.SAVE_EMPLOYE;??
  • [sql] view plaincopy
  • EXECUTE?EMPLOYE.SAVE_EMPLOYE;??

  • 結果為:
    Sql代碼
  • 雇員信息保存完成! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 雇員信息保存完成!??
  • ????????PL/SQL?過程已成功完成。??

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

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

  • 【訓練1】? 完整的雇員包EMP_PK的創建和應用。
    步驟1:在SQL*Plus中登錄SCOTT賬戶,輸入以下包頭和包體部分,按“執行”按鈕編譯:
    Sql代碼
  • CREATE?OR?REPLACE?PACKAGE?EMP_PK? ??
  • ????????--包頭部分???
  • ????????IS??
  • ????????V_EMP_COUNT?NUMBER(5);?????????????? ??
  • ????????--雇員人數??
  • ????????PROCEDURE?INIT(P_MAX?NUMBER,P_MIN?NUMBER);??--初始化??
  • ????????PROCEDURE?LIST_EMP;????????????????????? ??
  • ????????--顯示雇員列表??
  • PROCEDURE?INSERT_EMP(P_EMPNO????????NUMBER,P_ENAMEVARCHAR2,P_JOB?VARCHAR2, ??
  • ????????P_SAL?NUMBER);?????????????????????? ??
  • ????????--插入雇員??
  • ????????PROCEDURE?DELETE_EMP(P_EMPNO?NUMBER);???????--刪除雇員??
  • ?????????PROCEDURE?CHANGE_EMP_SAL(P_EMPNO?NUMBER,P_SAL?NUMBER);? ??
  • ????????--修改雇員工資??
  • ????????END?EMP_PK; ??
  • ????????/CREATE?OR?REPLACE?PACKAGE?BODY?EMP_PK ??
  • ?????????--包體部分???
  • ????????IS??
  • ????????V_MESSAGE?VARCHAR2(50);?--顯示信息??
  • V_MAX_SAL?NUMBER(7);?--工資上限??
  • ????????V_MIN_SAL?NUMBER(7);?--工資下限??
  • ????????FUNCTION?EXIST_EMP(P_EMPNO?NUMBER)??RETURN??BOOLEAN;?--判斷雇員是否存在函數??
  • ????????PROCEDURE?SHOW_MESSAGE;?--顯示信息過程??
  • ????????-------------------------------?初始化過程?----------------------------??
  • ????????PROCEDURE?INIT(P_MAX?NUMBER,P_MIN?NUMBER)? ??
  • ????????IS? ??
  • ????????BEGIN??
  • ?????????SELECT?COUNT(*)?INTO?V_EMP_COUNT?FROM?EMP; ??
  • V_MAX_SAL:=P_MAX; ??
  • ?????????V_MIN_SAL:=P_MIN; ??
  • ?????????V_MESSAGE:='初始化過程已經完成!'; ??
  • ?????????SHOW_MESSAGE;? ??
  • ????????END?INIT; ??
  • ----------------------------?顯示雇員列表過程?---------------------??
  • ????????PROCEDURE?LIST_EMP? ??
  • ?????????IS? ??
  • ????????BEGIN??
  • DBMS_OUTPUT.PUT_LINE('姓名???????職務??????工資'); ??
  • ????????FOR?emp_rec?IN?(SELECT?*?FROM?EMP) ??
  • ????????LOOP ??
  • ????DBMS_OUTPUT.PUT_LINE(RPAD(emp_rec.ename,10,'')||RPAD(emp_rec.job,10,'?')||TO_CHAR(emp_rec.sal)); ??
  • ?????????END?LOOP; ??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員總人數'||V_EMP_COUNT); ??
  • ????????END?LIST_EMP; ??
  • -----------------------------?插入雇員過程?-----------------------------??
  • ????????PROCEDUREINSERT_EMP(P_EMPNO?????NUMBER,P_ENAMEVARCHAR2,P_JOB????VARCHAR2,P_SAL?NUMBER) ??
  • ?????????IS? ??
  • ????????BEGIN??
  • ????????IF?NOT?EXIST_EMP(P_EMPNO)?THEN??
  • ????????INSERT?INTO?EMP(EMPNO,ENAME,JOB,SAL)????????VALUES(P_EMPNO,P_ENAME,P_JOB,P_SAL); ??
  • ????????COMMIT;? ??
  • ????????V_EMP_COUNT:=V_EMP_COUNT+1; ??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'已插入!'; ??
  • ????????ELSE??
  • V_MESSAGE:='雇員'||P_EMPNO||'已存在,不能插入!'; ??
  • ??????END?IF; ??
  • ?????SHOW_MESSAGE;? ??
  • ?????EXCEPTION ??
  • ????WHEN?OTHERS?THEN??
  • ?????V_MESSAGE:='雇員'||P_EMPNO||'插入失敗!'; ??
  • ?????SHOW_MESSAGE; ??
  • ?????END?INSERT_EMP; ??
  • ---------------------------?刪除雇員過程?--------------------??
  • ?????????PROCEDURE?DELETE_EMP(P_EMPNO?NUMBER)? ??
  • ????????IS? ??
  • ????????BEGIN? ??
  • ????????IF?EXIST_EMP(P_EMPNO)?THEN??
  • ????????DELETE?FROM?EMP?WHERE?EMPNO=P_EMPNO; ??
  • ????????COMMIT; ??
  • ?????????V_EMP_COUNT:=V_EMP_COUNT-1; ??
  • ?????????V_MESSAGE:='雇員'||P_EMPNO||'已刪除!'; ??
  • ?????????ELSE??
  • V_MESSAGE:='雇員'||P_EMPNO||'不存在,不能刪除!'; ??
  • ????END?IF; ??
  • ????SHOW_MESSAGE; ??
  • ?????EXCEPTION ??
  • ?????WHEN?OTHERS?THEN??
  • ?????V_MESSAGE:='雇員'||P_EMPNO||'刪除失敗!'; ??
  • ?????SHOW_MESSAGE; ??
  • ????END?DELETE_EMP; ??
  • ---------------------------------------?修改雇員工資過程?------------------------------------??
  • ????????PROCEDURE?CHANGE_EMP_SAL(P_EMPNO?NUMBER,P_SAL?NUMBER)? ??
  • ?????????IS? ??
  • ?????????BEGIN? ??
  • ?????????IF?(P_SAL>V_MAX_SAL?OR?P_SAL<V_MIN_SAL)?THEN??
  • ?????????V_MESSAGE:='工資超出修改范圍!'; ??
  • ????????ELSIF?NOT?EXIST_EMP(P_EMPNO)?THEN??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'不存在,不能修改工資!'; ??
  • ELSE??
  • ?????????UPDATE?EMP?SET?SAL=P_SAL?WHERE?EMPNO=P_EMPNO; ??
  • ????????COMMIT; ??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'工資已經修改!'; ??
  • ????????END?IF; ??
  • ????????SHOW_MESSAGE; ??
  • ????????EXCEPTION ??
  • ?????????WHEN?OTHERS?THEN??
  • ?????????V_MESSAGE:='雇員'||P_EMPNO||'工資修改失敗!'; ??
  • ?????????SHOW_MESSAGE; ??
  • ?????????END?CHANGE_EMP_SAL; ??
  • ----------------------------?顯示信息過程?----------------------------??
  • ?????????PROCEDURE?SHOW_MESSAGE? ??
  • ????????IS? ??
  • ????????BEGIN??
  • ?????????DBMS_OUTPUT.PUT_LINE('提示信息:'||V_MESSAGE); ??
  • ????????END?SHOW_MESSAGE; ??
  • ------------------------?判斷雇員是否存在函數?-------------------??
  • ?????????FUNCTION?EXIST_EMP(P_EMPNO?NUMBER) ??
  • ?????????RETURN?BOOLEAN? ??
  • ?????????IS??
  • ????????V_NUM?NUMBER;?--局部變量??
  • ????????BEGIN??
  • ????????SELECT?COUNT(*)?INTO?V_NUM?FROM?EMP?WHERE?EMPNO=P_EMPNO; ??
  • IF?V_NUM=1?THEN? ??
  • ???????????RETURN?TRUE; ??
  • ?????????ELSE??
  • ?????????RETURN?FALSE; ??
  • ????????END?IF;? ??
  • ????????END?EXIST_EMP; ??
  • ????????-----------------------------??
  • ????????END?EMP_PK;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?PACKAGE?EMP_PK???
  • ????????--包頭部分???
  • ????????IS??
  • ????????V_EMP_COUNT?NUMBER(5);????????????????
  • ????????--雇員人數??
  • ????????PROCEDURE?INIT(P_MAX?NUMBER,P_MIN?NUMBER);??--初始化??
  • ????????PROCEDURE?LIST_EMP;???????????????????????
  • ????????--顯示雇員列表??
  • PROCEDURE?INSERT_EMP(P_EMPNO????????NUMBER,P_ENAMEVARCHAR2,P_JOB?VARCHAR2,??
  • ????????P_SAL?NUMBER);????????????????????????
  • ????????--插入雇員??
  • ????????PROCEDURE?DELETE_EMP(P_EMPNO?NUMBER);???????--刪除雇員??
  • ?????????PROCEDURE?CHANGE_EMP_SAL(P_EMPNO?NUMBER,P_SAL?NUMBER);???
  • ????????--修改雇員工資??
  • ????????END?EMP_PK;??
  • ????????/CREATE?OR?REPLACE?PACKAGE?BODY?EMP_PK??
  • ?????????--包體部分???
  • ????????IS??
  • ????????V_MESSAGE?VARCHAR2(50);?--顯示信息??
  • V_MAX_SAL?NUMBER(7);?--工資上限??
  • ????????V_MIN_SAL?NUMBER(7);?--工資下限??
  • ????????FUNCTION?EXIST_EMP(P_EMPNO?NUMBER)??RETURN??BOOLEAN;?--判斷雇員是否存在函數??
  • ????????PROCEDURE?SHOW_MESSAGE;?--顯示信息過程??
  • ????????-------------------------------?初始化過程?----------------------------??
  • ????????PROCEDURE?INIT(P_MAX?NUMBER,P_MIN?NUMBER)???
  • ????????IS???
  • ????????BEGIN??
  • ?????????SELECT?COUNT(*)?INTO?V_EMP_COUNT?FROM?EMP;??
  • V_MAX_SAL:=P_MAX;??
  • ?????????V_MIN_SAL:=P_MIN;??
  • ?????????V_MESSAGE:='初始化過程已經完成!';??
  • ?????????SHOW_MESSAGE;???
  • ????????END?INIT;??
  • ----------------------------?顯示雇員列表過程?---------------------??
  • ????????PROCEDURE?LIST_EMP???
  • ?????????IS???
  • ????????BEGIN??
  • DBMS_OUTPUT.PUT_LINE('姓名???????職務??????工資');??
  • ????????FOR?emp_rec?IN?(SELECT?*?FROM?EMP)??
  • ????????LOOP??
  • ????DBMS_OUTPUT.PUT_LINE(RPAD(emp_rec.ename,10,'')||RPAD(emp_rec.job,10,'?')||TO_CHAR(emp_rec.sal));??
  • ?????????END?LOOP;??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員總人數'||V_EMP_COUNT);??
  • ????????END?LIST_EMP;??
  • -----------------------------?插入雇員過程?-----------------------------??
  • ????????PROCEDUREINSERT_EMP(P_EMPNO?????NUMBER,P_ENAMEVARCHAR2,P_JOB????VARCHAR2,P_SAL?NUMBER)??
  • ?????????IS???
  • ????????BEGIN??
  • ????????IF?NOT?EXIST_EMP(P_EMPNO)?THEN??
  • ????????INSERT?INTO?EMP(EMPNO,ENAME,JOB,SAL)????????VALUES(P_EMPNO,P_ENAME,P_JOB,P_SAL);??
  • ????????COMMIT;???
  • ????????V_EMP_COUNT:=V_EMP_COUNT+1;??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'已插入!';??
  • ????????ELSE??
  • V_MESSAGE:='雇員'||P_EMPNO||'已存在,不能插入!';??
  • ??????END?IF;??
  • ?????SHOW_MESSAGE;???
  • ?????EXCEPTION??
  • ????WHEN?OTHERS?THEN??
  • ?????V_MESSAGE:='雇員'||P_EMPNO||'插入失敗!';??
  • ?????SHOW_MESSAGE;??
  • ?????END?INSERT_EMP;??
  • ---------------------------?刪除雇員過程?--------------------??
  • ?????????PROCEDURE?DELETE_EMP(P_EMPNO?NUMBER)???
  • ????????IS???
  • ????????BEGIN???
  • ????????IF?EXIST_EMP(P_EMPNO)?THEN??
  • ????????DELETE?FROM?EMP?WHERE?EMPNO=P_EMPNO;??
  • ????????COMMIT;??
  • ?????????V_EMP_COUNT:=V_EMP_COUNT-1;??
  • ?????????V_MESSAGE:='雇員'||P_EMPNO||'已刪除!';??
  • ?????????ELSE??
  • V_MESSAGE:='雇員'||P_EMPNO||'不存在,不能刪除!';??
  • ????END?IF;??
  • ????SHOW_MESSAGE;??
  • ?????EXCEPTION??
  • ?????WHEN?OTHERS?THEN??
  • ?????V_MESSAGE:='雇員'||P_EMPNO||'刪除失敗!';??
  • ?????SHOW_MESSAGE;??
  • ????END?DELETE_EMP;??
  • ---------------------------------------?修改雇員工資過程?------------------------------------??
  • ????????PROCEDURE?CHANGE_EMP_SAL(P_EMPNO?NUMBER,P_SAL?NUMBER)???
  • ?????????IS???
  • ?????????BEGIN???
  • ?????????IF?(P_SAL>V_MAX_SAL?OR?P_SAL<V_MIN_SAL)?THEN??
  • ?????????V_MESSAGE:='工資超出修改范圍!';??
  • ????????ELSIF?NOT?EXIST_EMP(P_EMPNO)?THEN??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'不存在,不能修改工資!';??
  • ELSE??
  • ?????????UPDATE?EMP?SET?SAL=P_SAL?WHERE?EMPNO=P_EMPNO;??
  • ????????COMMIT;??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'工資已經修改!';??
  • ????????END?IF;??
  • ????????SHOW_MESSAGE;??
  • ????????EXCEPTION??
  • ?????????WHEN?OTHERS?THEN??
  • ?????????V_MESSAGE:='雇員'||P_EMPNO||'工資修改失敗!';??
  • ?????????SHOW_MESSAGE;??
  • ?????????END?CHANGE_EMP_SAL;??
  • ----------------------------?顯示信息過程?----------------------------??
  • ?????????PROCEDURE?SHOW_MESSAGE???
  • ????????IS???
  • ????????BEGIN??
  • ?????????DBMS_OUTPUT.PUT_LINE('提示信息:'||V_MESSAGE);??
  • ????????END?SHOW_MESSAGE;??
  • ------------------------?判斷雇員是否存在函數?-------------------??
  • ?????????FUNCTION?EXIST_EMP(P_EMPNO?NUMBER)??
  • ?????????RETURN?BOOLEAN???
  • ?????????IS??
  • ????????V_NUM?NUMBER;?--局部變量??
  • ????????BEGIN??
  • ????????SELECT?COUNT(*)?INTO?V_NUM?FROM?EMP?WHERE?EMPNO=P_EMPNO;??
  • IF?V_NUM=1?THEN???
  • ???????????RETURN?TRUE;??
  • ?????????ELSE??
  • ?????????RETURN?FALSE;??
  • ????????END?IF;???
  • ????????END?EXIST_EMP;??
  • ????????-----------------------------??
  • ????????END?EMP_PK;??
  • 結果為:
    Sql代碼
  • 程序包已創建。 ??
  • ????????程序包主體已創建。??
  • [sql] view plaincopy
  • 程序包已創建。??
  • ????????程序包主體已創建。??

  • 步驟2:初始化包:
    Sql代碼
  • SET?SERVEROUTPUT?ON??
  • EXECUTE?EMP_PK.INIT(6000,600);??
  • [sql] view plaincopy
  • SET?SERVEROUTPUT?ON??
  • EXECUTE?EMP_PK.INIT(6000,600);??

  • 顯示為:
    Sql代碼
  • 提示信息:初始化過程已經完成!??
  • [sql] view plaincopy
  • 提示信息:初始化過程已經完成!??

  • 步驟3:顯示雇員列表:
    Sql代碼
  • EXECUTE?EMP_PK.LIST_EMP;??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.LIST_EMP;??

  • 顯示為:
    Sql代碼
  • 姓名??????????職務??????????工資 ??
  • ????????SMITH???????CLERK???????1560 ??
  • ????????ALLEN???????SALESMAN????1936 ??
  • ????????WARD????????SALESMAN????1830 ??
  • ????????JONES???????MANAGER?????2975 ??
  • ????????... ??
  • ????????雇員總人數:14 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 姓名??????????職務??????????工資??
  • ????????SMITH???????CLERK???????1560??
  • ????????ALLEN???????SALESMAN????1936??
  • ????????WARD????????SALESMAN????1830??
  • ????????JONES???????MANAGER?????2975??
  • ????????...??
  • ????????雇員總人數:14??
  • ????????PL/SQL?過程已成功完成。??

  • 步驟4:插入一個新記錄:
    Sql代碼
  • EXECUTE?EMP_PK.INSERT_EMP(8001,'小王','CLERK',1000);??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.INSERT_EMP(8001,'小王','CLERK',1000);??

  • 顯示結果為:
    Sql代碼
  • 提示信息:雇員8001已插入! ??
  • PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 提示信息:雇員8001已插入!??
  • PL/SQL?過程已成功完成。??

  • 步驟5:通過全局變量V_EMP_COUNT查看雇員人數:
    Sql代碼
  • BEGIN??
  • DBMS_OUTPUT.PUT_LINE(EMP_PK.V_EMP_COUNT); ??
  • END;??
  • [sql] view plaincopy
  • BEGIN??
  • DBMS_OUTPUT.PUT_LINE(EMP_PK.V_EMP_COUNT);??
  • END;??

  • 顯示結果為:
    Sql代碼
  • 15 ??
  • PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 15??
  • PL/SQL?過程已成功完成。??

  • 步驟6:刪除新插入記錄:
    Sql代碼
  • EXECUTE?EMP_PK.DELETE_EMP(8001);??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.DELETE_EMP(8001);??

  • 顯示結果為:
    Sql代碼
  • 提示信息:雇員8001已刪除! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 提示信息:雇員8001已刪除!??
  • ????????PL/SQL?過程已成功完成。??

  • 再次刪除該雇員:
    Sql代碼
  • EXECUTE?EMP_PK.DELETE_EMP(8001);??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.DELETE_EMP(8001);??

  • 結果為:
    Sql代碼
  • 提示信息:雇員8001不存在,不能刪除!??
  • [sql] view plaincopy
  • 提示信息:雇員8001不存在,不能刪除!??

  • 步驟7:修改雇員工資:
    Sql代碼
  • EXECUTE?EMP_PK.CHANGE_EMP_SAL(7788,8000);??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.CHANGE_EMP_SAL(7788,8000);??

  • 顯示結果為:
    Sql代碼
  • 提示信息:工資超出修改范圍! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 提示信息:工資超出修改范圍!??
  • ????????PL/SQL?過程已成功完成。??

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

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

  • 結果為:
    Sql代碼
  • 提示信息:初始化過程已經完成! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 提示信息:初始化過程已經完成!??
  • ????????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代碼
  • CREATE?OR?REPLACE?PROCEDURE?EMP_COUNT ??
  • AS??
  • V_TOTAL?NUMBER(10); ??
  • BEGIN??
  • ?SELECT?COUNT(*)?INTO?V_TOTAL?FROM?EMP; ??
  • ?DBMS_OUTPUT.PUT_LINE('雇員總人數為:'||V_TOTAL); ??
  • END;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?PROCEDURE?EMP_COUNT??
  • AS??
  • V_TOTAL?NUMBER(10);??
  • BEGIN??
  • ?SELECT?COUNT(*)?INTO?V_TOTAL?FROM?EMP;??
  • ?DBMS_OUTPUT.PUT_LINE('雇員總人數為:'||V_TOTAL);??
  • END;??

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

  • 步驟4:調用存儲過程,在輸入區中輸入以下語句并執行:
    Sql代碼
  • EXECUTE?EMP_COUNT;??
  • [sql] view plaincopy
  • EXECUTE?EMP_COUNT;??

  • 顯示結果為:
    Sql代碼
  • 雇員總人數為:14 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 雇員總人數為:14??
  • ????????PL/SQL?過程已成功完成。??

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

  • Sql代碼
  • 授權成功。??
  • [sql] view plaincopy
  • 授權成功。??

  • 步驟3:登錄STUDENT賬戶,在SQL*Plus輸入區中輸入以下程序:
    Sql代碼
  • SET?SERVEROUTPUT?ON??
  • ????????BEGIN??
  • ????????SCOTT.EMP_COUNT; ??
  • ????????END;??
  • [sql] view plaincopy
  • SET?SERVEROUTPUT?ON??
  • ????????BEGIN??
  • ????????SCOTT.EMP_COUNT;??
  • ????????END;??

  • 步驟4:執行以上程序,結果為:
    Sql代碼
  • 雇員總人數為:14 ??
  • ????????PL/SQL?過程已成功完成。???
  • [sql] view plaincopy
  • 雇員總人數為:14??
  • ????????PL/SQL?過程已成功完成。???

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

  • 執行結果:
    Sql代碼
  • 過程已創建。??
  • [sql] view plaincopy
  • 過程已創建。??

  • 步驟2:調用存儲過程,在輸入區中輸入以下語句并執行:
    Sql代碼
  • EXECUTE?EMP_LIST??
  • [sql] view plaincopy
  • EXECUTE?EMP_LIST??

  • 顯示結果為:
    Sql代碼
  • 7369SMITH ??
  • 7499ALLEN ??
  • 7521WARD ??
  • 7566JONES ??
  • ????????????執行結果: ??
  • ????????雇員總人數為:14 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 7369SMITH??
  • 7499ALLEN??
  • 7521WARD??
  • 7566JONES??
  • ????????????執行結果:??
  • ????????雇員總人數為:14??
  • ????????PL/SQL?過程已成功完成。??

  • 說明:以上的EMP_LIST存儲過程中定義并使用了游標,用來循環顯示所有雇員的信息。然后調用已經成功編譯的存儲過程EMP_COUNT,用來附加顯示雇員總人數。通過EXECUTE命令來執行EMP_LIST存儲過程。
    【練習1】編寫顯示部門信息的存儲過程DEPT_LIST,要求統計出部門個數。
    參數傳遞
    參數的作用是向存儲過程傳遞數據,或從存儲過程獲得返回結果。正確的使用參數可以大大增加存儲過程的靈活性和通用性。
    參數的類型有三種,如下所示。
    Sql代碼
  • IN??定義一個輸入參數變量,用于傳遞參數給存儲過程 ??
  • OUT?定義一個輸出參數變量,用于從存儲過程獲取數據 ??
  • IN?OUT??定義一個輸入、輸出參數變量,兼有以上兩者的功能??
  • [sql] view plaincopy
  • IN??定義一個輸入參數變量,用于傳遞參數給存儲過程??
  • OUT?定義一個輸出參數變量,用于從存儲過程獲取數據??
  • 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代碼
  • CREATE?OR?REPLACE?PROCEDURE?CHANGE_SALARY(P_EMPNO?IN?NUMBER?DEFAULT?7788,P_RAISE?NUMBER?DEFAULT?10) ??
  • ????????AS??
  • ?????????V_ENAME?VARCHAR2(10); ??
  • V_SAL?NUMBER(5); ??
  • ????????BEGIN??
  • ????????SELECT?ENAME,SAL?INTO?V_ENAME,V_SAL?FROM?EMP?WHERE?EMPNO=P_EMPNO; ??
  • ?????????UPDATE?EMP?SET?SAL=SAL+P_RAISE?WHERE?EMPNO=P_EMPNO; ??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員'||V_ENAME||'的工資被改為'||TO_CHAR(V_SAL+P_RAISE)); ??
  • COMMIT; ??
  • ????????EXCEPTION ??
  • ?????????WHEN?OTHERS?THEN??
  • ????????DBMS_OUTPUT.PUT_LINE('發生錯誤,修改失敗!'); ??
  • ????????ROLLBACK; ??
  • ????????END;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?PROCEDURE?CHANGE_SALARY(P_EMPNO?IN?NUMBER?DEFAULT?7788,P_RAISE?NUMBER?DEFAULT?10)??
  • ????????AS??
  • ?????????V_ENAME?VARCHAR2(10);??
  • V_SAL?NUMBER(5);??
  • ????????BEGIN??
  • ????????SELECT?ENAME,SAL?INTO?V_ENAME,V_SAL?FROM?EMP?WHERE?EMPNO=P_EMPNO;??
  • ?????????UPDATE?EMP?SET?SAL=SAL+P_RAISE?WHERE?EMPNO=P_EMPNO;??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員'||V_ENAME||'的工資被改為'||TO_CHAR(V_SAL+P_RAISE));??
  • COMMIT;??
  • ????????EXCEPTION??
  • ?????????WHEN?OTHERS?THEN??
  • ????????DBMS_OUTPUT.PUT_LINE('發生錯誤,修改失敗!');??
  • ????????ROLLBACK;??
  • ????????END;??

  • 執行結果為:
    Sql代碼
  • 過程已創建。??
  • [sql] view plaincopy
  • 過程已創建。??

  • 步驟3:調用存儲過程,在輸入區中輸入以下語句并執行:
    Sql代碼
  • EXECUTE?CHANGE_SALARY(7788,80)??
  • [sql] view plaincopy
  • EXECUTE?CHANGE_SALARY(7788,80)??

  • 顯示結果為:
    Sql代碼
  • 雇員SCOTT的工資被改為3080???
  • [sql] view plaincopy
  • 雇員SCOTT的工資被改為3080???

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

  • 顯示結果為:
    Sql代碼
  • 雇員SCOTT的工資被改為3090???
  • [sql] view plaincopy
  • 雇員SCOTT的工資被改為3090???

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

  • 執行結果為:
    Sql代碼
  • 過程已創建。??
  • [sql] view plaincopy
  • 過程已創建。??

  • 步驟3:輸入以下程序并執行:
    Sql代碼
  • DECLARE??
  • ????????V_EMPCOUNT?NUMBER; ??
  • ????????BEGIN??
  • ????????EMP_COUNT(V_EMPCOUNT); ??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員總人數為:'||V_EMPCOUNT); ??
  • ????????END;??
  • [sql] view plaincopy
  • DECLARE??
  • ????????V_EMPCOUNT?NUMBER;??
  • ????????BEGIN??
  • ????????EMP_COUNT(V_EMPCOUNT);??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員總人數為:'||V_EMPCOUNT);??
  • ????????END;??

  • 顯示結果為:
    Sql代碼
  • 雇員總人數為:14 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 雇員總人數為:14??
  • ????????PL/SQL?過程已成功完成。??

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

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

  • 執行結果為:
    Sql代碼
  • 過程已創建。??
  • [sql] view plaincopy
  • 過程已創建。??

  • 步驟3:輸入以下程序并執行:
    Sql代碼
  • SET?SERVEROUTPUT?ON??
  • DECLARE??
  • V_PHONE_NUM?VARCHAR2(15); ??
  • BEGIN??
  • V_PHONE_NUM:='26731092'; ??
  • ADD_REGION(V_PHONE_NUM); ??
  • DBMS_OUTPUT.PUT_LINE('新的電話號碼:'||V_PHONE_NUM); ??
  • END;??
  • [sql] view plaincopy
  • SET?SERVEROUTPUT?ON??
  • DECLARE??
  • V_PHONE_NUM?VARCHAR2(15);??
  • BEGIN??
  • V_PHONE_NUM:='26731092';??
  • ADD_REGION(V_PHONE_NUM);??
  • DBMS_OUTPUT.PUT_LINE('新的電話號碼:'||V_PHONE_NUM);??
  • END;??

  • 顯示結果為:
    Sql代碼
  • 新的電話號碼:0755-26731092 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 新的電話號碼:0755-26731092??
  • ????????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代碼
  • CREATE?OR?REPLACE?FUNCTION?GET_EMP_NAME(P_EMPNO?NUMBER?DEFAULT?7788) ??
  • ????????RETURN?VARCHAR2 ??
  • ????????AS??
  • ?????????V_ENAME?VARCHAR2(10); ??
  • ????????BEGIN??
  • ????????ELECT?ENAME?INTO?V_ENAME?FROM?EMP?WHERE?EMPNO=P_EMPNO; ??
  • RETURN(V_ENAME); ??
  • EXCEPTION ??
  • ?WHEN?NO_DATA_FOUND?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('沒有該編號雇員!'); ??
  • ??RETURN?(NULL); ??
  • ?WHEN?TOO_MANY_ROWS?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('有重復雇員編號!'); ??
  • ??RETURN?(NULL); ??
  • ?WHEN?OTHERS?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('發生其他錯誤!'); ??
  • ??RETURN?(NULL); ??
  • END;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?FUNCTION?GET_EMP_NAME(P_EMPNO?NUMBER?DEFAULT?7788)??
  • ????????RETURN?VARCHAR2??
  • ????????AS??
  • ?????????V_ENAME?VARCHAR2(10);??
  • ????????BEGIN??
  • ????????ELECT?ENAME?INTO?V_ENAME?FROM?EMP?WHERE?EMPNO=P_EMPNO;??
  • RETURN(V_ENAME);??
  • EXCEPTION??
  • ?WHEN?NO_DATA_FOUND?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('沒有該編號雇員!');??
  • ??RETURN?(NULL);??
  • ?WHEN?TOO_MANY_ROWS?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('有重復雇員編號!');??
  • ??RETURN?(NULL);??
  • ?WHEN?OTHERS?THEN??
  • ??DBMS_OUTPUT.PUT_LINE('發生其他錯誤!');??
  • ??RETURN?(NULL);??
  • END;??

  • 步驟3:調用該存儲函數,輸入并執行以下程序:
    Sql代碼
  • BEGIN??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員7369的名稱是:'||?GET_EMP_NAME(7369)); ??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員7839的名稱是:'||?GET_EMP_NAME(7839)); ??
  • ????????END;??
  • [sql] view plaincopy
  • BEGIN??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員7369的名稱是:'||?GET_EMP_NAME(7369));??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員7839的名稱是:'||?GET_EMP_NAME(7839));??
  • ????????END;??

  • 顯示結果為:
    Sql代碼
  • 雇員7369的名稱是:SMITH ??
  • ????????雇員7839的名稱是:KING ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 雇員7369的名稱是:SMITH??
  • ????????雇員7839的名稱是:KING??
  • ????????PL/SQL?過程已成功完成。??

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

  • 結果為:
    Sql代碼
  • 名稱??????????????????????????????????????是否為空??類型 ??
  • ????????-------------------------------------------------------------?-------------?-----------------------??
  • ?NAME???????????????????????????????????????????????VARCHAR2(30) ??
  • ?TYPE???????????????????????????????????????????????VARCHAR2(12) ??
  • ?LINE???????????????????????????????????????????????NUMBER ??
  • ?TEXT???????????????????????????????????????????????VARCHAR2(4000)??
  • [sql] view plaincopy
  • 名稱??????????????????????????????????????是否為空??類型??
  • ????????-------------------------------------------------------------?-------------?-----------------------??
  • ?NAME???????????????????????????????????????????????VARCHAR2(30)??
  • ?TYPE???????????????????????????????????????????????VARCHAR2(12)??
  • ?LINE???????????????????????????????????????????????NUMBER??
  • ?TEXT???????????????????????????????????????????????VARCHAR2(4000)??

  • ? 說明:里面按行存放著過程或函數的腳本,NAME是過程或函數名,TYPE 代表類型(PROCEDURE或FUNCTION),LINE是行號,TEXT 為腳本。
    【訓練1】? 查詢過程EMP_COUNT的腳本。
    在SQL*Plus中輸入并執行如下查詢:
    Sql代碼
  • select?TEXT??from?user_source?WHERE?NAME='EMP_COUNT';??
  • [sql] view plaincopy
  • select?TEXT??from?user_source?WHERE?NAME='EMP_COUNT';??

  • 結果為:
    Sql代碼
  • TEXT ??
  • --------------------------------------------------------------------------------??
  • PROCEDURE?EMP_COUNT(P_TOTAL?OUT?NUMBER) ??
  • AS??
  • BEGIN??
  • ?SELECT?COUNT(*)?INTO?P_TOTAL?FROM?EMP; ??
  • END;??
  • [sql] view plaincopy
  • TEXT??
  • --------------------------------------------------------------------------------??
  • PROCEDURE?EMP_COUNT(P_TOTAL?OUT?NUMBER)??
  • AS??
  • BEGIN??
  • ?SELECT?COUNT(*)?INTO?P_TOTAL?FROM?EMP;??
  • END;??

  • 【訓練2】? 查詢過程GET_EMP_NAME的參數。
    在SQL*Plus中輸入并執行如下查詢:
    Sql代碼
  • DESCRIBE?GET_EMP_NAME??
  • [sql] view plaincopy
  • DESCRIBE?GET_EMP_NAME??

  • 結果為:
    Sql代碼
  • FUNCTION?GET_EMP_NAME?RETURNS?VARCHAR2 ??
  • ????????參數名稱????????????類型??????????輸入/輸出默認值? ??
  • ????????-----------------------------------------?-----------------------------------?-----------------?-------------??
  • ????????P_EMPNO?????????????NUMBER(4)?IN?????DEFAULT??
  • [sql] view plaincopy
  • FUNCTION?GET_EMP_NAME?RETURNS?VARCHAR2??
  • ????????參數名稱????????????類型??????????輸入/輸出默認值???
  • ????????-----------------------------------------?-----------------------------------?-----------------?-------------??
  • ????????P_EMPNO?????????????NUMBER(4)?IN?????DEFAULT??

  • 【訓練3】? 在發生編譯錯誤時,顯示錯誤。
    Sql代碼
  • SHOW?ERRORS??
  • [sql] view plaincopy
  • SHOW?ERRORS??

  • 以下是一段編譯錯誤顯示:
    Sql代碼
  • LINE/COL?ERROR ??
  • ????????-------------?-----------------------------------------------------------------??
  • ????????4/2???????PL/SQL:?SQL?Statement?ignored ??
  • ????????4/36??????PLS-00201:?必須說明標識符?'EMPP'??
  • [sql] view plaincopy
  • LINE/COL?ERROR??
  • ????????-------------?-----------------------------------------------------------------??
  • ????????4/2???????PL/SQL:?SQL?Statement?ignored??
  • ????????4/36??????PLS-00201:?必須說明標識符?'EMPP'??

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

  • 結果為:
    Sql代碼
  • STATUS ??
  • ????????------------??
  • ????????VALID??
  • [sql] view plaincopy
  • STATUS??
  • ????????------------??
  • ????????VALID??

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

  • 結果:
    Sql代碼
  • 名稱?????????????????????是否為空??類型 ??
  • ????????--------------------------------------------------------------?-------------?----------------------------??
  • ?????????NAME????????????NOT?NULL???VARCHAR2(30) ??
  • ?????????TYPE???????????????????????VARCHAR2(12) ??
  • ????????REFERENCED_OWNER????????????????????????????????VARCHAR2(30) ??
  • ?REFERENCED_NAME????????????????????????????????VARCHAR2(64) ??
  • ?REFERENCED_TYPE????????????????????????????????VARCHAR2(12) ??
  • REFERENCED_LINK_NAME????????????????????????????VARCHAR2(128) ??
  • ????????SCHEMAID????????????????????????????????????????NUMBER ??
  • ?????????DEPENDENCY_TYPE????????????????????????????????VARCHAR2(4)??
  • [sql] view plaincopy
  • 名稱?????????????????????是否為空??類型??
  • ????????--------------------------------------------------------------?-------------?----------------------------??
  • ?????????NAME????????????NOT?NULL???VARCHAR2(30)??
  • ?????????TYPE???????????????????????VARCHAR2(12)??
  • ????????REFERENCED_OWNER????????????????????????????????VARCHAR2(30)??
  • ?REFERENCED_NAME????????????????????????????????VARCHAR2(64)??
  • ?REFERENCED_TYPE????????????????????????????????VARCHAR2(12)??
  • REFERENCED_LINK_NAME????????????????????????????VARCHAR2(128)??
  • ????????SCHEMAID????????????????????????????????????????NUMBER??
  • ?????????DEPENDENCY_TYPE????????????????????????????????VARCHAR2(4)??

  • ? 說明:NAME為實體名,TYPE為實體類型,REFERENCED_OWNER為涉及到的實體擁有者賬戶,REFERENCED_NAME為涉及到的實體名,REFERENCED_TYPE 為涉及到的實體類型。
    【訓練5】? 查詢EMP_LIST存儲過程的依賴性。
    Sql代碼
  • SELECT?REFERENCED_NAME,REFERENCED_TYPE?FROM?USER_DEPENDENCIES?WHERE?NAME='EMP_LIST';??
  • [sql] view plaincopy
  • SELECT?REFERENCED_NAME,REFERENCED_TYPE?FROM?USER_DEPENDENCIES?WHERE?NAME='EMP_LIST';??

  • 執行結果:
    Sql代碼
  • REFERENCED_NAME?????????????????????????????????????????REFERENCED_TYPE ??
  • ????????------------------------------------------------------------------------------------------?----------------------------??
  • STANDARD????????????????????????????????????????????????PACKAGE ??
  • ????????SYS_STUB_FOR_PURITY_ANALYSIS????????????????????????????PACKAGE ??
  • ????????DBMS_OUTPUT?????????????????????????????????????????????????PACKAGE ??
  • ????????DBMS_OUTPUT?????????????????????????????????????????????SYNONYM ??
  • DBMS_OUTPUT??????????????????????NON-EXISTENT ??
  • ????????EMP?????????????????????????????????????????????????????????TABLE??
  • ????????EMP_COUNT???????????????????????????????????????????????????PROCEDURE??
  • [sql] view plaincopy
  • REFERENCED_NAME?????????????????????????????????????????REFERENCED_TYPE??
  • ????????------------------------------------------------------------------------------------------?----------------------------??
  • STANDARD????????????????????????????????????????????????PACKAGE??
  • ????????SYS_STUB_FOR_PURITY_ANALYSIS????????????????????????????PACKAGE??
  • ????????DBMS_OUTPUT?????????????????????????????????????????????????PACKAGE??
  • ????????DBMS_OUTPUT?????????????????????????????????????????????SYNONYM??
  • DBMS_OUTPUT??????????????????????NON-EXISTENT??
  • ????????EMP?????????????????????????????????????????????????????????TABLE??
  • ????????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代碼
  • 過程(PROCUDURE)???帶參數的命名的程序模塊 ??
  • 函數(FUNCTION)????帶參數、具有返回值的命名的程序模塊 ??
  • 變量(VARIABLE)????存儲變化的量的存儲單元 ??
  • 常量(CONSTANT)????存儲不變的量的存儲單元 ??
  • 游標(CURSOR)??用戶定義的數據操作緩存區,在可執行部分使用 ??
  • 類型(TYPE)????用戶定義的新的結構類型 ??
  • 異常(EXCEPTION)???在標準包中定義或由用戶自定義,用于處理程序錯誤??
  • [sql] view plaincopy
  • 過程(PROCUDURE)???帶參數的命名的程序模塊??
  • 函數(FUNCTION)????帶參數、具有返回值的命名的程序模塊??
  • 變量(VARIABLE)????存儲變化的量的存儲單元??
  • 常量(CONSTANT)????存儲不變的量的存儲單元??
  • 游標(CURSOR)??用戶定義的數據操作緩存區,在可執行部分使用??
  • 類型(TYPE)????用戶定義的新的結構類型??
  • 異常(EXCEPTION)???在標準包中定義或由用戶自定義,用于處理程序錯誤??

  • 說明部分可以出現在包的三個不同的部分:出現在包頭中的稱為公有元素,出現在包體中的稱為私有元素,出現在包體的過程(或函數)中的稱為局部變量。它們的性質有所不同,如下所示。
    Sql代碼
  • 公有元素(PUBLIC)????在包頭中說明,在包體中具體定義?在包外可見并可以訪問,對整個應用的全過程有效 ??
  • 私有元素(PRIVATE)???在包體的說明部分說明??只能被包內部的其他部分訪問 ??
  • 局部變量(LOCAL)?在過程或函數的說明部分說明???只能在定義變量的過程或函數中使用??
  • [sql] view plaincopy
  • 公有元素(PUBLIC)????在包頭中說明,在包體中具體定義?在包外可見并可以訪問,對整個應用的全過程有效??
  • 私有元素(PRIVATE)???在包體的說明部分說明??只能被包內部的其他部分訪問??
  • 局部變量(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代碼
  • DBMS_OUTPUT?在SQL*Plus環境下輸出信息 ??
  • DBMS_DDL????編譯過程函數和包 ??
  • DBMS_SESSION????改變用戶的會話,初始化包等 ??
  • DBMS_TRANSACTION????控制數據庫事務 ??
  • DBMS_MAIL???連接Oracle*Mail ??
  • DBMS_LOCK???進行復雜的鎖機制管理 ??
  • DBMS_ALERT??識別數據庫事件告警 ??
  • DBMS_PIPE???通過管道在會話間傳遞信息 ??
  • DBMS_JOB????管理Oracle的作業 ??
  • DBMS_LOB????操縱大對象 ??
  • DBMS_SQL????執行動態SQL語句??
  • [sql] view plaincopy
  • DBMS_OUTPUT?在SQL*Plus環境下輸出信息??
  • DBMS_DDL????編譯過程函數和包??
  • DBMS_SESSION????改變用戶的會話,初始化包等??
  • DBMS_TRANSACTION????控制數據庫事務??
  • DBMS_MAIL???連接Oracle*Mail??
  • DBMS_LOCK???進行復雜的鎖機制管理??
  • DBMS_ALERT??識別數據庫事件告警??
  • DBMS_PIPE???通過管道在會話間傳遞信息??
  • DBMS_JOB????管理Oracle的作業??
  • DBMS_LOB????操縱大對象??
  • DBMS_SQL????執行動態SQL語句??

  • 包的應用
    在SQL*Plus環境下,包和包體可以分別編譯,也可以一起編譯。如果分別編譯,則要先編譯包頭,后編譯包體。如果在一起編譯,則包頭寫在前,包體在后,中間用“/”分隔。
    可以將已經存在的存儲過程或函數添加到包中,方法是去掉過程或函數創建語句的CREATE OR REPLACE部分,將存儲過程或函數復制到包體中 ,然后重新編譯即可。
    ?? 如果需要將私有過程或函數變成共有過程或函數的話,將過程或函數說明部分復制到包頭說明部分,然后重新編譯就可以了。
    【訓練1】? 創建管理雇員信息的包EMPLOYE,它具有從EMP表獲得雇員信息,修改雇員名稱,修改雇員工資和寫回EMP表的功能。
    步驟1:登錄SCOTT賬戶,輸入以下代碼并編譯:
    Sql代碼
  • CREATE?OR?REPLACE?PACKAGE?EMPLOYE?--包頭部分???
  • ????????IS??
  • ?PROCEDURE?SHOW_DETAIL;? ??
  • ?PROCEDURE?GET_EMPLOYE(P_EMPNO?NUMBER);? ??
  • ?PROCEDURE?SAVE_EMPLOYE;? ??
  • ?PROCEDURE?CHANGE_NAME(P_NEWNAME?VARCHAR2);? ??
  • PROCEDURE?CHANGE_SAL(P_NEWSAL?NUMBER);? ??
  • ????????END?EMPLOYE; ??
  • ????????/ ??
  • ????????CREATE?OR?REPLACE?PACKAGE?BODY?EMPLOYE?--包體部分???
  • ????????IS??
  • ?EMPLOYE?EMP%ROWTYPE; ??
  • ????????--------------?顯示雇員信息?---------------??
  • ????????PROCEDURE?SHOW_DETAIL ??
  • ????????AS??
  • ????????BEGIN??
  • DBMS_OUTPUT.PUT_LINE(‘-----?雇員信息?-----’);?????
  • ????????DBMS_OUTPUT.PUT_LINE('雇員編號:'||EMPLOYE.EMPNO); ??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員名稱:'||EMPLOYE.ENAME); ??
  • ??????????DBMS_OUTPUT.PUT_LINE('雇員職務:'||EMPLOYE.JOB); ??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員工資:'||EMPLOYE.SAL); ??
  • ?????????DBMS_OUTPUT.PUT_LINE('部門編號:'||EMPLOYE.DEPTNO); ??
  • ????????END?SHOW_DETAIL; ??
  • -----------------?從EMP表取得一個雇員?--------------------??
  • ?????????PROCEDURE?GET_EMPLOYE(P_EMPNO?NUMBER) ??
  • ????????AS??
  • ????????BEGIN??
  • ????????SELECT?*?INTO?EMPLOYE?FROM?EMP?WHERE????EMPNO=P_EMPNO; ??
  • ????????DBMS_OUTPUT.PUT_LINE('獲取雇員'||EMPLOYE.ENAME||'信息成功'); ??
  • ?????????EXCEPTION ??
  • ?????????WHEN?OTHERS?THEN??
  • ???????????DBMS_OUTPUT.PUT_LINE('獲取雇員信息發生錯誤!'); ??
  • ????????END?GET_EMPLOYE; ??
  • ----------------------?保存雇員到EMP表?--------------------------??
  • ????????PROCEDURE?SAVE_EMPLOYE ??
  • ????????AS??
  • ????????BEGIN??
  • ????????UPDATE?EMP?SET?ENAME=EMPLOYE.ENAME,?SAL=EMPLOYE.SAL?WHERE?EMPNO= ??
  • ????EMPLOYE.EMPNO; ??
  • ?????DBMS_OUTPUT.PUT_LINE('雇員信息保存完成!'); ??
  • ????????END?SAVE_EMPLOYE; ??
  • ----------------------------?修改雇員名稱?------------------------------??
  • ????????PROCEDURE?CHANGE_NAME(P_NEWNAME?VARCHAR2) ??
  • ?????????AS??
  • ????????BEGIN??
  • ?????????EMPLOYE.ENAME:=P_NEWNAME; ??
  • ?????????DBMS_OUTPUT.PUT_LINE('修改名稱完成!'); ??
  • ????????END?CHANGE_NAME; ??
  • ----------------------------?修改雇員工資?--------------------------??
  • ????????PROCEDURE?CHANGE_SAL(P_NEWSAL?NUMBER) ??
  • ????????AS??
  • ????????BEGIN??
  • ?????????EMPLOYE.SAL:=P_NEWSAL; ??
  • ?????????DBMS_OUTPUT.PUT_LINE('修改工資完成!'); ??
  • ????????END?CHANGE_SAL; ??
  • ????????END?EMPLOYE;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?PACKAGE?EMPLOYE?--包頭部分???
  • ????????IS??
  • ?PROCEDURE?SHOW_DETAIL;???
  • ?PROCEDURE?GET_EMPLOYE(P_EMPNO?NUMBER);???
  • ?PROCEDURE?SAVE_EMPLOYE;???
  • ?PROCEDURE?CHANGE_NAME(P_NEWNAME?VARCHAR2);???
  • PROCEDURE?CHANGE_SAL(P_NEWSAL?NUMBER);???
  • ????????END?EMPLOYE;??
  • ????????/??
  • ????????CREATE?OR?REPLACE?PACKAGE?BODY?EMPLOYE?--包體部分???
  • ????????IS??
  • ?EMPLOYE?EMP%ROWTYPE;??
  • ????????--------------?顯示雇員信息?---------------??
  • ????????PROCEDURE?SHOW_DETAIL??
  • ????????AS??
  • ????????BEGIN??
  • DBMS_OUTPUT.PUT_LINE(‘-----?雇員信息?-----’);?????
  • ????????DBMS_OUTPUT.PUT_LINE('雇員編號:'||EMPLOYE.EMPNO);??
  • ????????DBMS_OUTPUT.PUT_LINE('雇員名稱:'||EMPLOYE.ENAME);??
  • ??????????DBMS_OUTPUT.PUT_LINE('雇員職務:'||EMPLOYE.JOB);??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員工資:'||EMPLOYE.SAL);??
  • ?????????DBMS_OUTPUT.PUT_LINE('部門編號:'||EMPLOYE.DEPTNO);??
  • ????????END?SHOW_DETAIL;??
  • -----------------?從EMP表取得一個雇員?--------------------??
  • ?????????PROCEDURE?GET_EMPLOYE(P_EMPNO?NUMBER)??
  • ????????AS??
  • ????????BEGIN??
  • ????????SELECT?*?INTO?EMPLOYE?FROM?EMP?WHERE????EMPNO=P_EMPNO;??
  • ????????DBMS_OUTPUT.PUT_LINE('獲取雇員'||EMPLOYE.ENAME||'信息成功');??
  • ?????????EXCEPTION??
  • ?????????WHEN?OTHERS?THEN??
  • ???????????DBMS_OUTPUT.PUT_LINE('獲取雇員信息發生錯誤!');??
  • ????????END?GET_EMPLOYE;??
  • ----------------------?保存雇員到EMP表?--------------------------??
  • ????????PROCEDURE?SAVE_EMPLOYE??
  • ????????AS??
  • ????????BEGIN??
  • ????????UPDATE?EMP?SET?ENAME=EMPLOYE.ENAME,?SAL=EMPLOYE.SAL?WHERE?EMPNO=??
  • ????EMPLOYE.EMPNO;??
  • ?????DBMS_OUTPUT.PUT_LINE('雇員信息保存完成!');??
  • ????????END?SAVE_EMPLOYE;??
  • ----------------------------?修改雇員名稱?------------------------------??
  • ????????PROCEDURE?CHANGE_NAME(P_NEWNAME?VARCHAR2)??
  • ?????????AS??
  • ????????BEGIN??
  • ?????????EMPLOYE.ENAME:=P_NEWNAME;??
  • ?????????DBMS_OUTPUT.PUT_LINE('修改名稱完成!');??
  • ????????END?CHANGE_NAME;??
  • ----------------------------?修改雇員工資?--------------------------??
  • ????????PROCEDURE?CHANGE_SAL(P_NEWSAL?NUMBER)??
  • ????????AS??
  • ????????BEGIN??
  • ?????????EMPLOYE.SAL:=P_NEWSAL;??
  • ?????????DBMS_OUTPUT.PUT_LINE('修改工資完成!');??
  • ????????END?CHANGE_SAL;??
  • ????????END?EMPLOYE;??

  • 步驟2:獲取雇員7788的信息:
    Sql代碼
  • SET?SERVEROUTPUT?ON??
  • ????????EXECUTE?EMPLOYE.GET_EMPLOYE(7788);??
  • [sql] view plaincopy
  • SET?SERVEROUTPUT?ON??
  • ????????EXECUTE?EMPLOYE.GET_EMPLOYE(7788);??

  • 結果為:
    Sql代碼
  • 獲取雇員SCOTT信息成功 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 獲取雇員SCOTT信息成功??
  • ????????PL/SQL?過程已成功完成。??

  • 步驟3:顯示雇員信息:
    Sql代碼
  • EXECUTE?EMPLOYE.SHOW_DETAIL;??
  • [sql] view plaincopy
  • EXECUTE?EMPLOYE.SHOW_DETAIL;??

  • 結果為:
    Sql代碼
  • ------------------?雇員信息?------------------??
  • ????????雇員編號:7788 ??
  • ????????雇員名稱:SCOTT ??
  • ????????雇員職務:ANALYST ??
  • ????????雇員工資:3000 ??
  • ????????部門編號:20 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • ------------------?雇員信息?------------------??
  • ????????雇員編號:7788??
  • ????????雇員名稱:SCOTT??
  • ????????雇員職務:ANALYST??
  • ????????雇員工資:3000??
  • ????????部門編號:20??
  • ????????PL/SQL?過程已成功完成。??

  • 步驟4:修改雇員工資:
    Sql代碼
  • EXECUTE?EMPLOYE.CHANGE_SAL(3800);??
  • [sql] view plaincopy
  • EXECUTE?EMPLOYE.CHANGE_SAL(3800);??

  • 結果為:
    Sql代碼
  • 修改工資完成! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 修改工資完成!??
  • ????????PL/SQL?過程已成功完成。??

  • 步驟5:將修改的雇員信息存入EMP表
    Sql代碼
  • EXECUTE?EMPLOYE.SAVE_EMPLOYE;??
  • [sql] view plaincopy
  • EXECUTE?EMPLOYE.SAVE_EMPLOYE;??

  • 結果為:
    Sql代碼
  • 雇員信息保存完成! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 雇員信息保存完成!??
  • ????????PL/SQL?過程已成功完成。??

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

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

  • 【訓練1】? 完整的雇員包EMP_PK的創建和應用。
    步驟1:在SQL*Plus中登錄SCOTT賬戶,輸入以下包頭和包體部分,按“執行”按鈕編譯:
    Sql代碼
  • CREATE?OR?REPLACE?PACKAGE?EMP_PK? ??
  • ????????--包頭部分???
  • ????????IS??
  • ????????V_EMP_COUNT?NUMBER(5);?????????????? ??
  • ????????--雇員人數??
  • ????????PROCEDURE?INIT(P_MAX?NUMBER,P_MIN?NUMBER);??--初始化??
  • ????????PROCEDURE?LIST_EMP;????????????????????? ??
  • ????????--顯示雇員列表??
  • PROCEDURE?INSERT_EMP(P_EMPNO????????NUMBER,P_ENAMEVARCHAR2,P_JOB?VARCHAR2, ??
  • ????????P_SAL?NUMBER);?????????????????????? ??
  • ????????--插入雇員??
  • ????????PROCEDURE?DELETE_EMP(P_EMPNO?NUMBER);???????--刪除雇員??
  • ?????????PROCEDURE?CHANGE_EMP_SAL(P_EMPNO?NUMBER,P_SAL?NUMBER);? ??
  • ????????--修改雇員工資??
  • ????????END?EMP_PK; ??
  • ????????/CREATE?OR?REPLACE?PACKAGE?BODY?EMP_PK ??
  • ?????????--包體部分???
  • ????????IS??
  • ????????V_MESSAGE?VARCHAR2(50);?--顯示信息??
  • V_MAX_SAL?NUMBER(7);?--工資上限??
  • ????????V_MIN_SAL?NUMBER(7);?--工資下限??
  • ????????FUNCTION?EXIST_EMP(P_EMPNO?NUMBER)??RETURN??BOOLEAN;?--判斷雇員是否存在函數??
  • ????????PROCEDURE?SHOW_MESSAGE;?--顯示信息過程??
  • ????????-------------------------------?初始化過程?----------------------------??
  • ????????PROCEDURE?INIT(P_MAX?NUMBER,P_MIN?NUMBER)? ??
  • ????????IS? ??
  • ????????BEGIN??
  • ?????????SELECT?COUNT(*)?INTO?V_EMP_COUNT?FROM?EMP; ??
  • V_MAX_SAL:=P_MAX; ??
  • ?????????V_MIN_SAL:=P_MIN; ??
  • ?????????V_MESSAGE:='初始化過程已經完成!'; ??
  • ?????????SHOW_MESSAGE;? ??
  • ????????END?INIT; ??
  • ----------------------------?顯示雇員列表過程?---------------------??
  • ????????PROCEDURE?LIST_EMP? ??
  • ?????????IS? ??
  • ????????BEGIN??
  • DBMS_OUTPUT.PUT_LINE('姓名???????職務??????工資'); ??
  • ????????FOR?emp_rec?IN?(SELECT?*?FROM?EMP) ??
  • ????????LOOP ??
  • ????DBMS_OUTPUT.PUT_LINE(RPAD(emp_rec.ename,10,'')||RPAD(emp_rec.job,10,'?')||TO_CHAR(emp_rec.sal)); ??
  • ?????????END?LOOP; ??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員總人數'||V_EMP_COUNT); ??
  • ????????END?LIST_EMP; ??
  • -----------------------------?插入雇員過程?-----------------------------??
  • ????????PROCEDUREINSERT_EMP(P_EMPNO?????NUMBER,P_ENAMEVARCHAR2,P_JOB????VARCHAR2,P_SAL?NUMBER) ??
  • ?????????IS? ??
  • ????????BEGIN??
  • ????????IF?NOT?EXIST_EMP(P_EMPNO)?THEN??
  • ????????INSERT?INTO?EMP(EMPNO,ENAME,JOB,SAL)????????VALUES(P_EMPNO,P_ENAME,P_JOB,P_SAL); ??
  • ????????COMMIT;? ??
  • ????????V_EMP_COUNT:=V_EMP_COUNT+1; ??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'已插入!'; ??
  • ????????ELSE??
  • V_MESSAGE:='雇員'||P_EMPNO||'已存在,不能插入!'; ??
  • ??????END?IF; ??
  • ?????SHOW_MESSAGE;? ??
  • ?????EXCEPTION ??
  • ????WHEN?OTHERS?THEN??
  • ?????V_MESSAGE:='雇員'||P_EMPNO||'插入失敗!'; ??
  • ?????SHOW_MESSAGE; ??
  • ?????END?INSERT_EMP; ??
  • ---------------------------?刪除雇員過程?--------------------??
  • ?????????PROCEDURE?DELETE_EMP(P_EMPNO?NUMBER)? ??
  • ????????IS? ??
  • ????????BEGIN? ??
  • ????????IF?EXIST_EMP(P_EMPNO)?THEN??
  • ????????DELETE?FROM?EMP?WHERE?EMPNO=P_EMPNO; ??
  • ????????COMMIT; ??
  • ?????????V_EMP_COUNT:=V_EMP_COUNT-1; ??
  • ?????????V_MESSAGE:='雇員'||P_EMPNO||'已刪除!'; ??
  • ?????????ELSE??
  • V_MESSAGE:='雇員'||P_EMPNO||'不存在,不能刪除!'; ??
  • ????END?IF; ??
  • ????SHOW_MESSAGE; ??
  • ?????EXCEPTION ??
  • ?????WHEN?OTHERS?THEN??
  • ?????V_MESSAGE:='雇員'||P_EMPNO||'刪除失敗!'; ??
  • ?????SHOW_MESSAGE; ??
  • ????END?DELETE_EMP; ??
  • ---------------------------------------?修改雇員工資過程?------------------------------------??
  • ????????PROCEDURE?CHANGE_EMP_SAL(P_EMPNO?NUMBER,P_SAL?NUMBER)? ??
  • ?????????IS? ??
  • ?????????BEGIN? ??
  • ?????????IF?(P_SAL>V_MAX_SAL?OR?P_SAL<V_MIN_SAL)?THEN??
  • ?????????V_MESSAGE:='工資超出修改范圍!'; ??
  • ????????ELSIF?NOT?EXIST_EMP(P_EMPNO)?THEN??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'不存在,不能修改工資!'; ??
  • ELSE??
  • ?????????UPDATE?EMP?SET?SAL=P_SAL?WHERE?EMPNO=P_EMPNO; ??
  • ????????COMMIT; ??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'工資已經修改!'; ??
  • ????????END?IF; ??
  • ????????SHOW_MESSAGE; ??
  • ????????EXCEPTION ??
  • ?????????WHEN?OTHERS?THEN??
  • ?????????V_MESSAGE:='雇員'||P_EMPNO||'工資修改失敗!'; ??
  • ?????????SHOW_MESSAGE; ??
  • ?????????END?CHANGE_EMP_SAL; ??
  • ----------------------------?顯示信息過程?----------------------------??
  • ?????????PROCEDURE?SHOW_MESSAGE? ??
  • ????????IS? ??
  • ????????BEGIN??
  • ?????????DBMS_OUTPUT.PUT_LINE('提示信息:'||V_MESSAGE); ??
  • ????????END?SHOW_MESSAGE; ??
  • ------------------------?判斷雇員是否存在函數?-------------------??
  • ?????????FUNCTION?EXIST_EMP(P_EMPNO?NUMBER) ??
  • ?????????RETURN?BOOLEAN? ??
  • ?????????IS??
  • ????????V_NUM?NUMBER;?--局部變量??
  • ????????BEGIN??
  • ????????SELECT?COUNT(*)?INTO?V_NUM?FROM?EMP?WHERE?EMPNO=P_EMPNO; ??
  • IF?V_NUM=1?THEN? ??
  • ???????????RETURN?TRUE; ??
  • ?????????ELSE??
  • ?????????RETURN?FALSE; ??
  • ????????END?IF;? ??
  • ????????END?EXIST_EMP; ??
  • ????????-----------------------------??
  • ????????END?EMP_PK;??
  • [sql] view plaincopy
  • CREATE?OR?REPLACE?PACKAGE?EMP_PK???
  • ????????--包頭部分???
  • ????????IS??
  • ????????V_EMP_COUNT?NUMBER(5);????????????????
  • ????????--雇員人數??
  • ????????PROCEDURE?INIT(P_MAX?NUMBER,P_MIN?NUMBER);??--初始化??
  • ????????PROCEDURE?LIST_EMP;???????????????????????
  • ????????--顯示雇員列表??
  • PROCEDURE?INSERT_EMP(P_EMPNO????????NUMBER,P_ENAMEVARCHAR2,P_JOB?VARCHAR2,??
  • ????????P_SAL?NUMBER);????????????????????????
  • ????????--插入雇員??
  • ????????PROCEDURE?DELETE_EMP(P_EMPNO?NUMBER);???????--刪除雇員??
  • ?????????PROCEDURE?CHANGE_EMP_SAL(P_EMPNO?NUMBER,P_SAL?NUMBER);???
  • ????????--修改雇員工資??
  • ????????END?EMP_PK;??
  • ????????/CREATE?OR?REPLACE?PACKAGE?BODY?EMP_PK??
  • ?????????--包體部分???
  • ????????IS??
  • ????????V_MESSAGE?VARCHAR2(50);?--顯示信息??
  • V_MAX_SAL?NUMBER(7);?--工資上限??
  • ????????V_MIN_SAL?NUMBER(7);?--工資下限??
  • ????????FUNCTION?EXIST_EMP(P_EMPNO?NUMBER)??RETURN??BOOLEAN;?--判斷雇員是否存在函數??
  • ????????PROCEDURE?SHOW_MESSAGE;?--顯示信息過程??
  • ????????-------------------------------?初始化過程?----------------------------??
  • ????????PROCEDURE?INIT(P_MAX?NUMBER,P_MIN?NUMBER)???
  • ????????IS???
  • ????????BEGIN??
  • ?????????SELECT?COUNT(*)?INTO?V_EMP_COUNT?FROM?EMP;??
  • V_MAX_SAL:=P_MAX;??
  • ?????????V_MIN_SAL:=P_MIN;??
  • ?????????V_MESSAGE:='初始化過程已經完成!';??
  • ?????????SHOW_MESSAGE;???
  • ????????END?INIT;??
  • ----------------------------?顯示雇員列表過程?---------------------??
  • ????????PROCEDURE?LIST_EMP???
  • ?????????IS???
  • ????????BEGIN??
  • DBMS_OUTPUT.PUT_LINE('姓名???????職務??????工資');??
  • ????????FOR?emp_rec?IN?(SELECT?*?FROM?EMP)??
  • ????????LOOP??
  • ????DBMS_OUTPUT.PUT_LINE(RPAD(emp_rec.ename,10,'')||RPAD(emp_rec.job,10,'?')||TO_CHAR(emp_rec.sal));??
  • ?????????END?LOOP;??
  • ?????????DBMS_OUTPUT.PUT_LINE('雇員總人數'||V_EMP_COUNT);??
  • ????????END?LIST_EMP;??
  • -----------------------------?插入雇員過程?-----------------------------??
  • ????????PROCEDUREINSERT_EMP(P_EMPNO?????NUMBER,P_ENAMEVARCHAR2,P_JOB????VARCHAR2,P_SAL?NUMBER)??
  • ?????????IS???
  • ????????BEGIN??
  • ????????IF?NOT?EXIST_EMP(P_EMPNO)?THEN??
  • ????????INSERT?INTO?EMP(EMPNO,ENAME,JOB,SAL)????????VALUES(P_EMPNO,P_ENAME,P_JOB,P_SAL);??
  • ????????COMMIT;???
  • ????????V_EMP_COUNT:=V_EMP_COUNT+1;??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'已插入!';??
  • ????????ELSE??
  • V_MESSAGE:='雇員'||P_EMPNO||'已存在,不能插入!';??
  • ??????END?IF;??
  • ?????SHOW_MESSAGE;???
  • ?????EXCEPTION??
  • ????WHEN?OTHERS?THEN??
  • ?????V_MESSAGE:='雇員'||P_EMPNO||'插入失敗!';??
  • ?????SHOW_MESSAGE;??
  • ?????END?INSERT_EMP;??
  • ---------------------------?刪除雇員過程?--------------------??
  • ?????????PROCEDURE?DELETE_EMP(P_EMPNO?NUMBER)???
  • ????????IS???
  • ????????BEGIN???
  • ????????IF?EXIST_EMP(P_EMPNO)?THEN??
  • ????????DELETE?FROM?EMP?WHERE?EMPNO=P_EMPNO;??
  • ????????COMMIT;??
  • ?????????V_EMP_COUNT:=V_EMP_COUNT-1;??
  • ?????????V_MESSAGE:='雇員'||P_EMPNO||'已刪除!';??
  • ?????????ELSE??
  • V_MESSAGE:='雇員'||P_EMPNO||'不存在,不能刪除!';??
  • ????END?IF;??
  • ????SHOW_MESSAGE;??
  • ?????EXCEPTION??
  • ?????WHEN?OTHERS?THEN??
  • ?????V_MESSAGE:='雇員'||P_EMPNO||'刪除失敗!';??
  • ?????SHOW_MESSAGE;??
  • ????END?DELETE_EMP;??
  • ---------------------------------------?修改雇員工資過程?------------------------------------??
  • ????????PROCEDURE?CHANGE_EMP_SAL(P_EMPNO?NUMBER,P_SAL?NUMBER)???
  • ?????????IS???
  • ?????????BEGIN???
  • ?????????IF?(P_SAL>V_MAX_SAL?OR?P_SAL<V_MIN_SAL)?THEN??
  • ?????????V_MESSAGE:='工資超出修改范圍!';??
  • ????????ELSIF?NOT?EXIST_EMP(P_EMPNO)?THEN??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'不存在,不能修改工資!';??
  • ELSE??
  • ?????????UPDATE?EMP?SET?SAL=P_SAL?WHERE?EMPNO=P_EMPNO;??
  • ????????COMMIT;??
  • ????????V_MESSAGE:='雇員'||P_EMPNO||'工資已經修改!';??
  • ????????END?IF;??
  • ????????SHOW_MESSAGE;??
  • ????????EXCEPTION??
  • ?????????WHEN?OTHERS?THEN??
  • ?????????V_MESSAGE:='雇員'||P_EMPNO||'工資修改失敗!';??
  • ?????????SHOW_MESSAGE;??
  • ?????????END?CHANGE_EMP_SAL;??
  • ----------------------------?顯示信息過程?----------------------------??
  • ?????????PROCEDURE?SHOW_MESSAGE???
  • ????????IS???
  • ????????BEGIN??
  • ?????????DBMS_OUTPUT.PUT_LINE('提示信息:'||V_MESSAGE);??
  • ????????END?SHOW_MESSAGE;??
  • ------------------------?判斷雇員是否存在函數?-------------------??
  • ?????????FUNCTION?EXIST_EMP(P_EMPNO?NUMBER)??
  • ?????????RETURN?BOOLEAN???
  • ?????????IS??
  • ????????V_NUM?NUMBER;?--局部變量??
  • ????????BEGIN??
  • ????????SELECT?COUNT(*)?INTO?V_NUM?FROM?EMP?WHERE?EMPNO=P_EMPNO;??
  • IF?V_NUM=1?THEN???
  • ???????????RETURN?TRUE;??
  • ?????????ELSE??
  • ?????????RETURN?FALSE;??
  • ????????END?IF;???
  • ????????END?EXIST_EMP;??
  • ????????-----------------------------??
  • ????????END?EMP_PK;??
  • 結果為:
    Sql代碼
  • 程序包已創建。 ??
  • ????????程序包主體已創建。??
  • [sql] view plaincopy
  • 程序包已創建。??
  • ????????程序包主體已創建。??

  • 步驟2:初始化包:
    Sql代碼
  • SET?SERVEROUTPUT?ON??
  • EXECUTE?EMP_PK.INIT(6000,600);??
  • [sql] view plaincopy
  • SET?SERVEROUTPUT?ON??
  • EXECUTE?EMP_PK.INIT(6000,600);??

  • 顯示為:
    Sql代碼
  • 提示信息:初始化過程已經完成!??
  • [sql] view plaincopy
  • 提示信息:初始化過程已經完成!??

  • 步驟3:顯示雇員列表:
    Sql代碼
  • EXECUTE?EMP_PK.LIST_EMP;??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.LIST_EMP;??

  • 顯示為:
    Sql代碼
  • 姓名??????????職務??????????工資 ??
  • ????????SMITH???????CLERK???????1560 ??
  • ????????ALLEN???????SALESMAN????1936 ??
  • ????????WARD????????SALESMAN????1830 ??
  • ????????JONES???????MANAGER?????2975 ??
  • ????????... ??
  • ????????雇員總人數:14 ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 姓名??????????職務??????????工資??
  • ????????SMITH???????CLERK???????1560??
  • ????????ALLEN???????SALESMAN????1936??
  • ????????WARD????????SALESMAN????1830??
  • ????????JONES???????MANAGER?????2975??
  • ????????...??
  • ????????雇員總人數:14??
  • ????????PL/SQL?過程已成功完成。??

  • 步驟4:插入一個新記錄:
    Sql代碼
  • EXECUTE?EMP_PK.INSERT_EMP(8001,'小王','CLERK',1000);??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.INSERT_EMP(8001,'小王','CLERK',1000);??

  • 顯示結果為:
    Sql代碼
  • 提示信息:雇員8001已插入! ??
  • PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 提示信息:雇員8001已插入!??
  • PL/SQL?過程已成功完成。??

  • 步驟5:通過全局變量V_EMP_COUNT查看雇員人數:
    Sql代碼
  • BEGIN??
  • DBMS_OUTPUT.PUT_LINE(EMP_PK.V_EMP_COUNT); ??
  • END;??
  • [sql] view plaincopy
  • BEGIN??
  • DBMS_OUTPUT.PUT_LINE(EMP_PK.V_EMP_COUNT);??
  • END;??

  • 顯示結果為:
    Sql代碼
  • 15 ??
  • PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 15??
  • PL/SQL?過程已成功完成。??

  • 步驟6:刪除新插入記錄:
    Sql代碼
  • EXECUTE?EMP_PK.DELETE_EMP(8001);??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.DELETE_EMP(8001);??

  • 顯示結果為:
    Sql代碼
  • 提示信息:雇員8001已刪除! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 提示信息:雇員8001已刪除!??
  • ????????PL/SQL?過程已成功完成。??

  • 再次刪除該雇員:
    Sql代碼
  • EXECUTE?EMP_PK.DELETE_EMP(8001);??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.DELETE_EMP(8001);??

  • 結果為:
    Sql代碼
  • 提示信息:雇員8001不存在,不能刪除!??
  • [sql] view plaincopy
  • 提示信息:雇員8001不存在,不能刪除!??

  • 步驟7:修改雇員工資:
    Sql代碼
  • EXECUTE?EMP_PK.CHANGE_EMP_SAL(7788,8000);??
  • [sql] view plaincopy
  • EXECUTE?EMP_PK.CHANGE_EMP_SAL(7788,8000);??

  • 顯示結果為:
    Sql代碼
  • 提示信息:工資超出修改范圍! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 提示信息:工資超出修改范圍!??
  • ????????PL/SQL?過程已成功完成。??

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

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

  • 結果為:
    Sql代碼
  • 提示信息:初始化過程已經完成! ??
  • ????????PL/SQL?過程已成功完成。??
  • [sql] view plaincopy
  • 提示信息:初始化過程已經完成!??
  • ????????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存储过程实例的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。