ORACLE存儲過程詳解

ORACLE存儲過程詳解

一、定義

所謂存儲過程(Stored Procedure),就是一組用於完成特定數據庫功能的SQL語句集,該SQL語句集通過編譯後存儲在數據庫系統中。在使用時候,用戶經過指定已經定義的存儲過程名字並給出相應的存儲過程參數來調用並執行它,從而完成一個或一系列的數據庫操做。html

二、存儲過程的建立

Oracle存儲過程包含三部分:過程聲明,執行過程部分,存儲過程異常。node

(1)無參存儲過程語法

 
1
2
3
4
5
6
7
8
create or replace procedure NoParPro 
  as  //聲明 
 
  begin // 執行 
 
  exception//存儲過程異常 
 
  end ;

(2)帶參存儲過程實例

 
1
2
3
4
5
6
7
8
9
create or replace procedure queryempname(sfindno emp.empno%type)  
as 
    sName emp.ename%type; 
    sjob emp.job%type; 
begin 
        .... 
exception 
        .... 
end ;

(3)帶參數存儲過程含賦值方式

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
create or replace procedure runbyparmeters   
     (isal in emp.sal%type,  
      sname out varchar
      sjob in out varchar
  as  
     icount number; 
  begin 
       select count (*) into icount from emp where sal>isal and job=sjob; 
       if icount=1 then 
         .... 
       else 
        .... 
      end if; 
exception 
      when too_many_rows then 
      DBMS_OUTPUT.PUT_LINE( '返回值多於1行' ); 
      when others then 
      DBMS_OUTPUT.PUT_LINE( '在RUNBYPARMETERS過程當中出錯!' ); 
end ;

其中參數IN表示輸入參數,是參數的默認模式。sql

OUT表示返回值參數,類型可使用任意Oracle中的合法類型。數據庫

OUT模式定義的參數只能在過程體內部賦值,表示該參數能夠將某個值傳遞迴調用他的過程session

IN OUT表示該參數能夠向該過程當中傳遞值,也能夠將某個值傳出去。oracle

(4)存儲過程當中遊標定義使用

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
as //定義(遊標一個能夠遍歷的結果集)  
CURSOR cur_1 IS  
   SELECT area_code,CMCODE, SUM (rmb_amt)/10000 rmb_amt_sn, 
          SUM (usd_amt)/10000 usd_amt_sn  
   FROM BGD_AREA_CM_M_BASE_T  
   WHERE ym >= vs_ym_sn_beg  
        AND ym <= vs_ym_sn_end  
   GROUP BY area_code,CMCODE;  
 
begin //執行(經常使用 For 語句遍歷遊標)      
FOR rec IN cur_1 LOOP  
   UPDATE xxxxxxxxxxx_T  
    SET rmb_amt_sn = rec.rmb_amt_sn,usd_amt_sn = rec.usd_amt_sn  
    WHERE area_code = rec.area_code  
    AND CMCODE = rec.CMCODE  
    AND ym = is_ym;  
END LOOP;

(5)遊標的定義

 
1
2
3
4
5
6
7
8
9
10
11
12
--顯示cursor的處理
declare 
---聲明cursor,建立和命名一個sql工做區
cursor cursor_name is 
     select real_name from account_hcz;
     v_realname varchar2(20);
begin
     open cursor_name; ---打開cursor,執行sql語句產生的結果集
     fetch cursor_name into v_realname; --提取cursor,提取結果集中的記錄
     dbms_output.put_line(v_realname);
     close cursor_name; --關閉cursor
end ;

三、在Oracle中對存儲過程的調用

(1)過程調用方式一

 
1
2
3
4
5
6
7
8
9
10
11
declare 
       realsal emp.sal%type; 
       realname varchar (40); 
       realjob varchar (40); 
begin   //過程調用開始 
       realsal:=1100; 
       realname:= ''
       realjob:= 'CLERK'
       runbyparmeters(realsal,realname,realjob);--必須按順序 
       DBMS_OUTPUT.PUT_LINE(REALNAME|| '   ' ||REALJOB); 
END ;  //過程調用結束

(2)過程調用方式二

 
1
2
3
4
5
6
7
8
9
10
11
12
declare 
      realsal emp.sal%type; 
      realname varchar (40); 
      realjob varchar (40); 
begin    //過程調用開始 
      realsal:=1100; 
      realname:= ''
      realjob:= 'CLERK'
      --指定值對應變量順序可變 
      runbyparmeters(sname=>realname,isal=>realsal,sjob=>realjob);          
     DBMS_OUTPUT.PUT_LINE(REALNAME|| '   ' ||REALJOB); 
END ;  //過程調用結束

(3)過程調用方式三(SQL命令行方式下)

一、SQL>exec proc_emp(‘參數1’,’參數2’);//無返回值過程調用app

二、SQL>var vsal number函數

SQL> exec proc_emp (‘參數1’,:vsal);// 有返回值過程調用fetch

或者:call proc_emp (‘參數1’,:vsal);// 有返回值過程調用this

存儲過程建立語法

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

注意事項

存儲過程參數不帶取值範圍,in表示傳入,out表示輸出; 變量帶取值範圍,後面接分號; 在判斷語句前最好先用count(*)函數判斷是否存在該條操做記錄; 用select … into … 給變量賦值; 在代碼中拋異經常使用 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
2
3
4
5
6
7
8
9
10
CREATE OR REPLACE PROCEDURE 存儲過程名字
(
     參數1 IN NUMBER,
     參數2 IN NUMBER
) IS
變量1 INTEGER :=0;
變量2 DATE ;
BEGIN
     --執行體
END 存儲過程名字;

2. SELECT INTO STATEMENT

將select查詢的結果存入到變量中,能夠同時將多個列存儲多個變量中,必須有一條記錄,不然拋出異常(若是沒有記錄拋出NO_DATA_FOUND)

例子:

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

3. IF 判斷

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

4. while 循環

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

5. 變量賦值

 
1
V_TEST := 123;

6. 用for in 使用cursor

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

7. 帶參數的cursor

 
1
2
3
4
5
   CURSOR C_USER(C_ID NUMBER) IS SELECT NAME FROM USER WHERE TYPEID=C_ID;
   OPEN C_USER(變量值);
FETCH C_USER INTO V_NAME;
   EXIT WHEN FETCH C_USER%NOTFOUND;
CLOSE C_USER;

8. 用pl/sql developer debug

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

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

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

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

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

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

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

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

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

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

若是在存儲過程當中,使用以下語句:

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

若是A表中不存在bid=」xxxxxx」的記錄,則fcount=null(即便fcount定義時設置了默認值,如:fcount number(8):=0依然無效,fcount仍是會變成null),這樣之後使用fcount時就可能有問題,因此在這裏最好先判斷一下:

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

這樣就一切ok了。

6.Hibernate調用oracle存儲過程

 
1
2
3
4
5
6
7
8
9
10
11
12
this.pnumberManager.getHibernateTemplate(). execute (
             new HibernateCallback() {
                 public Object doInHibernate(Session session)
                         throws HibernateException, SQLException {
                     CallableStatement cs = session
                             . connection ()
                             .prepareCall( "{call modifyapppnumber_remain()}" );
                     cs.setString(1, foundationid);
                     cs. execute ();
                     return null ;
                 }
             });

 

轉載自:https://www.2cto.com/database/201802/723493.html

相關文章
相關標籤/搜索