ORACLE 存儲過程詳解(轉)


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

本文轉自 https://blog.csdn.net/zezezuiaiya/article/details/79557621

打開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