1. 程式人生 > >Oracle儲存過程詳解(一)

Oracle儲存過程詳解(一)

儲存過程建立語法:

    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. 儲存過程引數不帶取值範圍,in表示傳入,out表示輸出
  2. 變數帶取值範圍,後面接分號
  3. 在判斷語句前最好先用count(*)函式判斷是否存在該條操作記錄
  4. 用select … into … 給變數賦值
  5. 在程式碼中拋異常用 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(變數值);
  LOOP
 FETCH C_USER INTO V_NAME;
 EXIT FETCH C_USER%NOTFOUND;
    do something
  END LOOP;
  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;
                    }
                });