所謂存儲過程(Stored Procedure),就是一組用於完成特定數據庫功能的SQL語句集,該SQL語句集通過編譯後存儲在數據庫系統中。在使用時候,用戶經過指定已經定義的存儲過程名字並給出相應的存儲過程參數來調用並執行它,從而完成一個或一系列的數據庫操做。html
Oracle存儲過程包含三部分:過程聲明,執行過程部分,存儲過程異常。node
1
2
3
4
5
6
7
8
|
create
or
replace
procedure
NoParPro
as
//聲明
;
begin
// 執行
;
exception//存儲過程異常
;
end
;
|
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
;
|
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
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;
|
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
;
|
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
; //過程調用結束
|
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
; //過程調用結束
|
一、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
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
存儲過程名字;
|
將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
;
|
1
2
3
4
5
|
IF V_TEST = 1
THEN
BEGIN
do something
END
;
END
IF;
|
1
2
3
4
5
|
WHILE V_TEST=1 LOOP
BEGIN
XXXX
END
;
END
LOOP;
|
1
|
V_TEST := 123;
|
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
;
|
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;
|
鏈接數據庫後創建一個Test WINDOW,在窗口輸入調用SP的代碼,F9開始debug,CTRL+N單步調試
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
;
}
});
|