1. 程式人生 > >Oracle 儲存過程語法以及遊標的定義及INTO語法的使用

Oracle 儲存過程語法以及遊標的定義及INTO語法的使用

儲存過程建立語法:

    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

注意事項:

  1. 儲存過程引數不帶取值範圍,in表示傳入,out表示輸出
  2. 變數帶取值範圍,後面接分號
  3. 在判斷語句前最好先用count(*)函式判斷是否存在該條操作記錄
  4. 用select … into … 給變數賦值
  5. 在程式碼中拋異常用 raise+異常名

已命名的異常:

命名的系統異常產生原因
ACCESS_INTO_NULL未定義物件
CASE_NOT_FOUNDCASE 中若未包含相應的 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_DENIEDPL/SQL 應用程式連線到 oracle 資料庫時,提供了不正確的使用者名稱或密碼
NOT_LOGGED_ONPL/SQL 應用程式在沒有連線 oralce 資料庫的情況下訪問資料
PROGRAM_ERRORPL/SQL 內部問題,可能需要重灌資料字典& pl./SQL系統包
ROWTYPE_MISMATCH宿主遊標變數與 PL/SQL 遊標變數的返回型別不相容
SELF_IS_NULL使用物件型別時,在 null 物件上呼叫物件方法
STORAGE_ERROR執行 PL/SQL 時,超出記憶體空間
SYS_INVALID_ID無效的 ROWID 字串
TIMEOUT_ON_RESOURCEOracle 在等待資源時超時

基本語法

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;