1. 程式人生 > >Oracle-procedure/cursor解讀

Oracle-procedure/cursor解讀

概述

procedure系列

procedure概述

儲存過程( Stored Procedure )是一組為了完成特定功能的 SQL 語句集,經編譯後儲存在資料庫中。

使用者通過指定儲存過程的名字並給出引數(如果該儲存過程帶有引數)來執行它。

儲存過程是由流控制和 SQL 語句書寫的過程,這個過程經編譯和優化後儲存在資料庫伺服器中,應用程式使用時只要呼叫即可。

在 ORACLE 中,若干個有聯絡的過程可以組合在一起構成程式包。

procedure優點

  • 儲存過程只在創造時進行編譯,以後每次執行儲存過程都不需再重新編譯,而一般 SQL 語句每執行一次就編譯一次,所以使用儲存過程可提高資料庫執行速度。

  • 當對資料庫進行復雜操作時(如對多個表進行 Update、Insert、Query、Delete時),可將此複雜操作用儲存過程封裝起來與資料庫提供的事務處理結合一起使用。

  • 儲存過程可以重複使用,可減少資料庫開發人員的工作量。

  • 安全性高,可設定只有某使用者才具有對指定儲存過程的使用權。

和function的區別

這裡寫圖片描述

procedure栗子

CREATE OR REPLACE procedure proc_trade(
       v_tradeid     in number, --交易id
       v_third_ip    in varchar2, --第三方ip
       v_third_time  in
date, --第三方完成時間 v_thire_state in number, --第三方狀態 o_result out number, --返回值 o_detail out varchar2 --詳細描述 )
as -- 定義變數 v_error varchar2(500);
begin --對變數賦值 o_result := 0; o_detail := '驗證失敗'; --業務邏輯處理 if v_tradeid > 100 then insert into table_name (.. .) values (.. .); commit; elsif v_tradeid < 100
and v_tradeid > 50 then insert into table_name (.. .) values (.. .); commit; else goto log; end if; --跳轉標誌符,名稱自己指定 <<log>> o_result := 1; --捕獲異常 exception when no_data_found then result := 2; when dup_val_on_index then result := 3; when others then result := -1; end proc_trade;

引數型別可以自己指定,這種寫法可行,但是最好使用%type 來獲取引數的型別(table_name.column_name%TYPE)。 這樣就不會出現引數型別的錯誤。

儲存過程中的迴圈

for … in … loop 迴圈

迴圈遍歷遊標

示例1:

CREATE OR REPLACE PROCEDURE proc_test AS
  CURSOR c1 IS
    SELECT * FROM dat_trade;
BEGIN
  FOR x IN c1 LOOP
    DBMS_OUTPUT.put_line(x.id);
  END LOOP;
END proc_test;

示例 2:

CREATE OR REPLACE PROCEDURE proc_test AS
BEGIN
  FOR x IN (SELECT power_id FROM sys_power) LOOP
    DBMS_OUTPUT.put_line(x.power_id);
  END LOOP;
END proc_test;

根據數值進行迴圈

栗子一

CREATE OR REPLACE PROCEDURE proc_test AS
BEGIN
  for x in 1 .. 100 loop
    dbms_output.put_line(x);
  end loop;
END proc_test;

栗子2:在過程裡指定輸入引數 v_num. 在呼叫過程時指定迴圈次數。

CREATE OR REPLACE PROCEDURE proc_test(v_num IN NUMBER) AS
BEGIN
  FOR x IN 1 .. v_num LOOP
    DBMS_OUTPUT.put_line(x);
  END LOOP;
END proc_test;

loop 迴圈

LOOP
DELETE FROM orders
WHERE senddate < TO_CHAR (ADD_MONTHS (SYSDATE, -3),
'yyyy-mm-dd')
AND ROWNUM < 1000;
EXIT WHEN SQL%ROWCOUNT < 1;
COMMIT;
END LOOP;

這 裡 的 SQL%ROWCOUNT 是 隱 士 遊 標 。 除 了 這 個 , 還 有 其 他 幾
個: %found, %notfound, %isopen。

while 迴圈

CREATE OR REPLACE PROCEDURE proc_test(v_num IN NUMBER) 
  AS
  i NUMBER := 1;
BEGIN
  WHILE i < v_num LOOP
    BEGIN
      i := i + 1;
      DBMS_OUTPUT.put_line(i);
    END;
  END LOOP;
END proc_test;

儲存過程中的判斷

if … elsif … else … 判斷

CREATE OR REPLACE PROCEDURE proc_test(v_num IN NUMBER) AS
BEGIN
  IF v_num < 10 THEN
    DBMS_OUTPUT.put_line(v_num);
  ELSIF v_num > 10 AND v_num < 50 THEN
    DBMS_OUTPUT.put_line(v_num - 10);
  ELSE
    DBMS_OUTPUT.put_line(v_num - 50);
  END IF;
END proc_test;

case … when … end case 判斷

CREATE OR REPLACE PROCEDURE proc_test(v_num IN NUMBER) AS
BEGIN
  case v_num
    when 1 then
      DBMS_OUTPUT.put_line(v_num);
    when 2 then
      DBMS_OUTPUT.put_line(v_num);
    when 3 then
      DBMS_OUTPUT.put_line(v_num);
    else
      null;
  end case;
END proc_test;

遊標

Cursor 型遊標(不能用於引數傳遞)

CREATE OR REPLACE PROCEDURE proc_test AS
  CURSOR c1 IS
    SELECT * FROM dat_trade;
BEGIN
  FOR x IN c1 LOOP
    DBMS_OUTPUT.put_line(x.id);
  END LOOP;
END proc_test;

SYS_REFCURSOR 型遊標

該遊標是 Oracle 預先定義的遊標,可作出引數進行傳遞。

SYS_REFCURSOR 只能通過 OPEN 方法來開啟和賦值

我們可以使用這種類似的遊標來返回一個結果集:

CREATE OR REPLACE procedure proc_test(
    checknum   in number, --每次返回的資料量
    ref_cursor out sys_refcursor --返回的結果集,遊標
    ) as
begin
  open ref_cursor for
    select *
      from (select * from dat_trade where state = 41 order by id)
     where rownum < checknum;
end proc_test;

SYS_REFCURSOR 中可使用四個狀態屬性:

  • ( 1) . %NOTFOUND(未找到記錄資訊)
  • ( 2) . %FOUND(找到記錄資訊)
  • ( 3) . %ROWCOUNT(然後當前遊標所指向的行位置)
  • (4). %ISOPEN(是否開啟)
CREATE OR REPLACE PROCEDURE proc_test(
    checknum   IN NUMBER, --每次返回的資料量
    ref_cursor OUT sys_refcursor --返回的結果集,遊標
    ) AS
  t_tmp table_name%ROWTYPE;
BEGIN
  OPEN ref_cursor FOR
    SELECT *
      FROM (SELECT * FROM table_name WHERE state = 41 ORDER BY id)
     WHERE ROWNUM < checknum;
  --循環遊標
  LOOP
    FETCH ref_cursor
      INTO t_tmp;
    EXIT WHEN ref_cursor%NOTFOUND;
    -- DBMS_OUTPUT.put_line (t_tmp.id);
    UPDATE table_name SET state = 53 WHERE id = t_tmp.id;
    COMMIT;
  END LOOP;
  CLOSE ref_cursor;
END proc_test;