1、定義
所謂存儲過程(Stored Procedure),就是一組用於完成特定數據庫功能的SQL語句集,該SQL語句集經過編譯后存儲在數據庫系統中。在使用時候,用戶通過指定已經定義的存儲過程名字並給出相應的存儲過程參數來調用並執行它,從而完成一個或一系列的數據庫操作。
2、存儲過程的創建
Oracle存儲過程包含三部分:過程聲明,執行過程部分,存儲過程異常。
(1)無參存儲過程語法
create or replace procedure NoParPro as //聲明 ; begin // 執行 ; exception//存儲過程異常 ; end;
(2)帶參存儲過程實例
create or replace procedure queryempname(sfindno emp.empno%type) as sName emp.ename%type; sjob emp.job%type; begin .... exception .... end;
(3)帶參數存儲過程含賦值方式
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表示輸入參數,是參數的默認模式。
OUT表示返回值參數,類型可以使用任意Oracle中的合法類型。
OUT模式定義的參數只能在過程體內部賦值,表示該參數可以將某個值傳遞回調用他的過程
IN OUT表示該參數可以向該過程中傳遞值,也可以將某個值傳出去。
(4)存儲過程中游標定義使用
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)游標的定義
--顯示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;
3、在Oracle中對存儲過程的調用
(1)過程調用方式一
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)過程調用方式二
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命令行方式下)
1、SQL>exec proc_emp(‘參數1’,’參數2’);//無返回值過程調用
2、SQL>var vsal number
SQL> exec proc_emp (‘參數1’,:vsal);// 有返回值過程調用
或者:call proc_emp (‘參數1’,:vsal);// 有返回值過程調用
存儲過程創建語法
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. 基本結構
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)
例子:
BEGIN SELECT col1,col2 into 變量1,變量2 FROM typestruct where xxx; EXCEPTION WHEN NO_DATA_FOUND THEN xxxx; END;
3. IF 判斷
IF V_TEST = 1 THEN BEGIN do something END; END IF;
4. while 循環
WHILE V_TEST=1 LOOP BEGIN XXXX END; END LOOP;
5. 變量賦值
V_TEST := 123;
6. 用for in 使用cursor
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
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,如:
select a.appname from appinfo a;-- 正確 select a.appname from appinfo as a;-- 錯誤
也許,是怕和oracle中的存儲過程中的關鍵字as沖突的問題吧
2.在存儲過程中,select某一字段時,后面必須緊跟into,如果select整個記錄,利用游標的話就另當別論了。
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.在存儲過程中,別名不能和字段名稱相同,否則雖然編譯可以通過,但在運行階段會報錯
--正確 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,定義如下:
create table A( id varchar2(50) primary key not null, vcount number(8) not null, bid varchar2(50) not null -- 外鍵 );
如果在存儲過程中,使用如下語句:
select sum(vcount) into fcount from A where bid='xxxxxx';
如果A表中不存在bid=”xxxxxx”的記錄,則fcount=null(即使fcount定義時設置了默認值,如:fcount number(8):=0依然無效,fcount還是會變成null),這樣以后使用fcount時就可能有問題,所以在這里最好先判斷一下:
if fcount is null then fcount:=0; end if;
這樣就一切ok了。
6.Hibernate調用oracle存儲過程
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; } });