Oracle/PLSQL存儲過程詳解


原文鏈接:https://blog.csdn.net/zezezuiaiya/article/details/79557621

Oracle/PLSQL存儲過程詳解

一.在plsql中創建一個存儲過程

打開plsql,右鍵procedures,新建。
新建
如果新建毫無反應
直接文件-新建-程序窗口-空白,新建一個程序窗口: 
新建2


存儲過程創建語法:

  1. create [or replace] procedure 存儲過程名(param1 in type,param2 out type)
  2. as
  3. 變量 1 類型(值范圍);
  4. 變量2 類型(值范圍);
  5. Begin
  6. Select count(*) into 變量1 from 表A where列名=param1;
  7.  
  8. If (判斷條件) then
  9. Select 列名 into 變量2 from 表A where列名=param1;
  10. Dbms_output。Put_line(‘打印信息’);
  11. Elsif (判斷條件) then
  12. Dbms_output。Put_line(‘打印信息’);
  13. Else
  14. Raise 異常名(NO_DATA_FOUND);
  15. End if;
  16.  
  17. Exception
  18. When others then
  19. Rollback;
  20. End;

二:實例:寫存儲過程(例子)

  1. --創建一個名為p_contract_purchase_import的存儲過程
  2. create or replace procedure p_contract_purchase_import(
  3. --以下寫存儲過程的外部參數(傳入的參數)
  4. --格式為:參數名 in 參數類型
  5. --注意,這里的varchar不標注大小
  6. V_IN_SUBCOMPANYID in VARCHAR2, --專業分公司
  7. V_IN_PURCONTRACTMONEY in NUMBER, --采購合同金額
  8. V_IN_PARTYBNAME in VARCHAR2, --供應商名稱
  9. --設置一個返回值
  10. v_o_ret out number --返回結果0:成功;1:失敗; 4:查不到供應商; 5:添加關聯失敗;6:新增采購合同失敗
  11.  
  12. )
  13.  
  14. --以下寫內部參數
  15. --格式為:參數名稱 參數類型
  16. --注意,這里的varchar需要標注大小
  17. as
  18. V_SUPPLIERID INTEGER; --供應商編號
  19. V_PARTYBACCOUNT VARCHAR2(100); --收款賬號
  20. V_SQLERRM VARCHAR2(4000); --錯誤詳情
  21.  
  22. --存儲過程開始
  23. begin
  24. --為某些變量賦初值
  25. --格式為 變量名 := 值
  26. v_o_ret := 1;
  27. V_SUPPLIERID := '';
  28. V_PARTYBACCOUNT := '';
  29.  
  30. --寫具體的操作語句(sql)
  31. --if語句
  32. if(V_IN_PARTYBNAME is not null) then
  33. begin
  34. select t.SUPPLIERID,t.PARTYBACCOUNT,t.PARTYBBANK ,t.PARTYBNAME
  35. into V_SUPPLIERID,V_PARTYBACCOUNT,V_PARTYBBANK,V_PARTYBNAME
  36. from T_SUPPLIER t where t.PARTYBNAME=trim(V_IN_PARTYBNAME) and t.SUBCOMPANYID=trim(V_IN_SUBCOMPANYID);
  37. --拋異常
  38. exception
  39. when others then
  40. v_o_ret := 4 ; --找不到該供應商
  41. V_PARTYBNAME := V_IN_PARTYBNAME;
  42.  
  43. -- 將異常原因寫入存儲過程日志表
  44. V_SQLERRM := SQLERRM;
  45. INSERT INTO T_LOG_DBERR
  46. (ERRTIME, ERRMODEL, ERRDESC)
  47. VALUES
  48. (SYSDATE,
  49. 'PROCEDURES',
  50. 'p_contract_purchase_import:ret=' || v_o_ret ||','||
  51. V_SQLERRM);
  52. COMMIT;
  53. end ;
  54. end if;
  55.  
  56. ······
  57.  
  58. end ;
  59. commit;
  60. v_o_ret :=0 ;
  61.  
  62.  
  63. return;
  64.  
  65. EXCEPTION
  66. WHEN OTHERS THEN
  67. ROLLBACK;
  68. -- 插入異常原因
  69. V_SQLERRM := SQLERRM;
  70. INSERT INTO T_LOG_DBERR
  71. (ERRTIME, ERRMODEL, ERRDESC)
  72. VALUES
  73. (SYSDATE,
  74. 'PROCEDURES',
  75. 'p_contract_purchase_import:ret=' || v_o_ret ||','||
  76. V_SQLERRM);
  77. COMMIT;
  78.  
  79. --存儲過程結束
  80. end p_contract_purchase_import;

1.注意begin 和 end成對 

2.務必將錯誤內容打印出來。否則,有的時候,就算調試看出來時哪一個語句的錯誤,也鬧不明白錯在哪里

三.存儲過程調試

1.按F8或是菜單欄第三行第二個執行按鍵編譯存儲過程。此時,如果有語法上的明顯錯誤,plsql會給予提示。 
2.在procedures中找到要調試的存儲過程,右鍵,選測試。注意,要記得勾選添加調試信息啊。 
開啟調試 
3.打開調試窗口,填寫輸入參數 
4.點測試窗口的調試按鍵(如圖中畫圈的位置)開始調試 
填參數並打開調試
5.逐步調試,如下圖: 
調試
圖中1:點擊單步調試,主要就是使用這個來進行調試。執行到的代碼會高亮顯示,此外,注意圖中4的位置,可以在這里輸入變量的名字,來查看變量的當前值。 
圖中2/3:分別為跳出單步執行和全部執行,調試時不推薦使用。

單步調試過程中,如果執行到哪一步直接跳轉到了exception結束,那么,就是這一步出來問題,可以記住這個位置,再次調試,通過查看這附近變量的值,以及查看錯誤日志記錄的錯誤詳情,來確認出錯的具體原因並進行修改。

四.在java中調用

存儲過程調試好之后,就可以在java中調用該存儲過程。

  1. logger .info("調用存儲過程p_contract_purchase_import");
  2. //存儲過程名稱。有多少個傳入參數打幾個問號(包括v_o_ret)
  3. String procName= "{Call p_contract_purchase_import(?,?,?,?) }";
  4. DataSource ds = SessionFactoryUtils .getDataSource(this.getHibernateTemplate().getSessionFactory());
  5. Connection conn = null;
  6. CallableStatement call = null;
  7. //ResultSet rs =null;
  8.  
  9. try
  10. {
  11. //創建連接
  12. conn = ds .getConnection();
  13. call = conn.prepareCall(procName);
  14. //傳入數據
  15. call.setString(1, (importList.get(0)).trim());
  16. if(ratio_amount==null) {
  17. call.setString(2,null);
  18. } else {
  19. call.setLong(2, ratio_amount);
  20. }
  21. call.setString(3, (importList.get(2)).trim());
  22.  
  23. //第四個參數是作為返回值存在的
  24. call.registerOutParameter(4, Types.BIGINT);
  25. //執行存儲過程
  26. call.executeUpdate();
  27. //獲取返回的結果
  28. ret = call.getInt(17) ;
  29. logger .info("ret:"+ret); ;//call.getInt(6));
  30.  
  31. try
  32. {
  33. //關閉連接
  34. call.close();
  35. call = null ;
  36. conn .close();
  37. conn = null ;
  38. } catch (SQLException e) {
  39. // TODO Auto-generated catch block
  40. }
  41.  
  42. } catch (SQLException e){
  43. logger .error("打開存儲過程錯誤:",e);
  44. }
  45. finally{
  46. try {
  47. if (call != null) {
  48. call.close();
  49. }
  50. if (conn != null) {
  51. conn .close();
  52. }
  53. } catch (SQLException e) {
  54. // TODO Auto-generated catch block
  55. conn = null;
  56. }
  57. }

需要注意的是,call.setLong()不可以傳入空值。如果內容有可能為空的話,set之前需要判斷是否為空,不為空才能使用setLong()方法,否則,要使用setString方法。


五:注意事項:

  1. 存儲過程參數不帶取值范圍,in表示傳入,out表示輸出
  2. 變量帶取值范圍,后面接分號
  3. 在判斷語句前最好先用count(*)函數判斷是否存在該條操作記錄
  4. 用select … into … 給變量賦值
  5. 在代碼中拋異常用 raise+異常名

已命名的異常:

命名的系統異常 產生原因
ACCESS_INTO_NULL 未定義對象
CASE_NOT_FOUND CASE 中若未包含相應的 WHEN ,並且沒有設置ELSE 時
COLLECTION_IS_NULL 集合元素未初始化
CURSER_ALREADY_OPEN 游標已經打開
DUP_VAL_ON_INDEX 唯一索引對應的列上有重復的值
INVALID_CURSOR 在不合法的游標上進行操作
INVALID_NUMBER 內嵌的 SQL 語句不能將字符轉換為數字
NO_DATA_FOUND 使用 select into 未返回行,或應用索引表未初始化的
TOO_MANY_ROWS 執行 select into 時,結果集超過一行
ZERO_DIVIDE 除數為 0
SUBSCRIPT_BEYOND_COUNT 元素下標超過嵌套表或 VARRAY 的最大值
SUBSCRIPT_OUTSIDE_LIMIT 使用嵌套表或 VARRAY 時,將下標指定為負數
VALUE_ERROR 賦值時,變量長度不足以容納實際數據
LOGIN_DENIED PL/SQL 應用程序連接到 oracle 數據庫時,提供了不正確的用戶名或密碼
NOT_LOGGED_ON PL/SQL 應用程序在沒有連接 oralce 數據庫的情況下訪問數據
PROGRAM_ERROR PL/SQL 內部問題,可能需要重裝數據字典& pl./SQL系統包
ROWTYPE_MISMATCH 宿主游標變量與 PL/SQL 游標變量的返回類型不兼容
SELF_IS_NULL 使用對象類型時,在 null 對象上調用對象方法
STORAGE_ERROR 運行 PL/SQL 時,超出內存空間
SYS_INVALID_ID 無效的 ROWID 字符串
TIMEOUT_ON_RESOURCE Oracle 在等待資源時超時

六:基本語法

1. 基本結構

  1. CREATE OR REPLACE PROCEDURE 存儲過程名字
  2. (
  3. 參數1 IN NUMBER,
  4. 參數2 IN NUMBER
  5. ) IS
  6. 變量1 INTEGER :=0;
  7. 變量 2 DATE;
  8. BEGIN
  9. --執行體
  10. END 存儲過程名字;

2. SELECT INTO STATEMENT

將select查詢的結果存入到變量中,可以同時將多個列存儲多個變量中,必須有一條記錄,否則拋出異常(如果沒有記錄拋出NO_DATA_FOUND) 
例子:

  1. BEGIN
  2. SELECT col1,col2 into 變量1,變量2 FROM typestruct where xxx;
  3. EXCEPTION
  4. WHEN NO_DATA_FOUND THEN
  5. xxxx;
  6. END;

3. IF 判斷

  1. IF V_TEST=1 THEN
  2. BEGIN
  3. do something
  4. END;
  5. END IF;

4. while 循環

  1. WHILE V_TEST=1 LOOP
  2. BEGIN
  3. XXXX
  4. END;
  5. END LOOP;

5. 變量賦值

 V_TEST := 123;

6. 用for in 使用cursor

  1. IS
  2. CURSOR cur IS SELECT * FROM xxx;
  3. BEGIN
  4. FOR cur_result in cur LOOP
  5. BEGIN
  6. V_SUM :=cur_result.列名 1+cur_result.列名2
  7. END;
  8. END LOOP;
  9. END;

7. 帶參數的cursor

  1. CURSOR C_USER(C_ID NUMBER) IS SELECT NAME FROM USER WHERE TYPEID=C_ID;
  2. OPEN C_USER(變量值);
  3. LOOP
  4. FETCH C_USER INTO V_NAME;
  5. EXIT FETCH C_USER%NOTFOUND;
  6. do something
  7. END LOOP;
  8. CLOSE C_USER;

8. 用pl/sql developer debug

連接數據庫后建立一個Test WINDOW,在窗口輸入調用SP的代碼,F9開始debug,CTRL+N單步調試

八:關於oracle存儲過程的若干問題備忘

1.在oracle中,數據表別名不能加as,如:

  1. select a.appname from appinfo a;-- 正確
  2. select a.appname from appinfo as a;-- 錯誤

也許,是怕和oracle中的存儲過程中的關鍵字as沖突的問題吧

2.在存儲過程中,select某一字段時,后面必須緊跟into,如果select整個記錄,利用游標的話就另當別論了。

  1. select af.keynode into kn from APPFOUNDATION af
  2. where af.appid=aid and af.foundationid=fid;-- 有into,正確編譯
  3. select af.keynode from APPFOUNDATION af
  4. where af.appid=aid and af.foundationid=fid;-- 沒有into,編譯報錯,提示:Compilation
  5.  
  6. Error: PLS-00428: an INTO clause is expected in this SELECT statement

3.在利用select…into…語法時,必須先確保數據庫中有該條記錄,否則會報出”no data found”異常。

可以在該語法之前,先利用select count(*) from 查看數據庫中是否存在該記錄,如果存在,再利用select…into…

4.在存儲過程中,別名不能和字段名稱相同,否則雖然編譯可以通過,但在運行階段會報錯

  1. --正確
  2. select keynode into kn from APPFOUNDATION where appid=aid and foundationid=fid;
  3.  
  4. --錯誤
  5. select af.keynode into kn from APPFOUNDATION af
  6. where af.appid=appid and af.foundationid=foundationid;
  7. -- 運行階段報錯,提示ORA-01422:exact fetch returns more than requested number of rows

5.在存儲過程中,關於出現null的問題

假設有一個表A,定義如下:

  1. create table A(
  2. id varchar2(50) primary key not null,
  3. vcount number(8) not null,
  4. bid varchar2(50) not null -- 外鍵
  5. );

如果在存儲過程中,使用如下語句:

select sum(vcount) into fcount from A where bid='xxxxxx';

如果A表中不存在bid=”xxxxxx”的記錄,則fcount=null(即使fcount定義時設置了默認值,如:fcount number(8):=0依然無效,fcount還是會變成null),這樣以后使用fcount時就可能有問題,所以在這里最好先判斷一下:

  1. if fcount is null then
  2. fcount:= 0;
  3. end if;

這樣就一切ok了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM