Oracle 儲存過程語法以及遊標的定義及INTO語法的使用
阿新 • • 發佈:2019-01-31
儲存過程建立語法:
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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
注意事項:
- 儲存過程引數不帶取值範圍,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 儲存過程名字;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
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;
- 1
- 2
- 3
- 4
- 5
- 6
3. IF 判斷
IF V_TEST=1 THEN
BEGIN
do something
END;
END IF;
- 1
- 2
- 3
- 4
- 5
4. while 迴圈
WHILE V_TEST=1 LOOP
BEGIN
XXXX
END;
END LOOP;
- 1
- 2
- 3
- 4
- 5
5. 變數賦值
V_TEST := 123;
- 1
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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
7. 帶引數的cursor
CURSOR C_USER(C_ID NUMBER) IS SELECT NAME FROM USER WHERE TYPEID=C_ID;
OPEN C_USER(變數值);
LOOP
FETCH C_USER INTO V_NAME;
EXIT FETCH C_USER%NOTFOUND;
do something
END LOOP;
CLOSE C_USER;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
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;-- 錯誤
- 1
- 2
也許,是怕和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
- 1
- 2
- 3
- 4
- 5
- 6
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
5.在儲存過程中,關於出現null的問題
假設有一個表A,定義如下:
create table A(
id varchar2(50) primary key not null,
vcount number(8) not null,
bid varchar2(50) not null -- 外來鍵
);
- 1
- 2
- 3
- 4
- 5
如果在儲存過程中,使用如下語句:
select sum(vcount) into fcount from A where bid='xxxxxx';
- 1
如果A表中不存在bid=”xxxxxx”的記錄,則fcount=null(即使fcount定義時設定了預設值,如:fcount number(8):=0依然無效,fcount還是會變成null),這樣以後使用fcount時就可能有問題,所以在這裡最好先判斷一下:
if fcount is null then
fcount:=0;
end if;
- 1
- 2
- 3
6 遊標:
CREATE OR REPLACE PROCEDURE cursorvariabletest(p_table IN VARCHAR2) IS
TYPE t_emp_dept IS REF CURSOR; --定義遊標變數型別
v_cursorvar t_emp_dept; --宣告遊標變數
v_empno emp.empno%TYPE;
v_ename emp.ename%TYPE;
v_job emp.job%TYPE;
v_dname dept.dname%TYPE;
v_loc dept.loc%TYPE;
BEGIN
IF p_table = 'emp' THEN
v_empno := 7369;
OPEN v_cursorvar FOR --定義遊標名字
SELECT ename, job FROM emp WHERE empno = v_empno; --開啟遊標變數
ELSE
IF p_table = 'dept' THEN
OPEN v_cursorvar FOR
SELECT dname, loc FROM dept; --開啟遊標變數
ELSE
raise_application_error(-20000, '請輸入emp或dept!');
END IF;
END IF;
LOOP
IF p_table = 'emp' THEN
FETCH v_cursorvar
INTO v_ename, v_job;
EXIT WHEN v_cursorvar%NOTFOUND;
dbms_output.put_line('ename=' || v_ename || ',job=' || v_job);
ELSE
FETCH v_cursorvar
INTO v_dname, v_loc;
EXIT WHEN v_cursorvar%NOTFOUND;
dbms_output.put_line('dname=' || v_dname || ',loc=' || v_loc);
END IF;
END LOOP;
CLOSE v_cursorvar; --關閉遊標變數
END;