【開發篇plsql】plsql遊標

yellowlee發表於2010-06-05

4,遊標

經典的資料庫程式設計中,用來處理返回一個查詢結果時使用的內部資料結構叫做遊標。

Plsql的遊標可以分為隱式遊標和顯式遊標。

顯式遊標由使用者顯式宣告,遊標將指向活動集中的當前行,可以顯式的開啟,提取,關閉。例如:

SQL> declare

  2    cursor cur_emp is

  3      select * from scott.emp;

  4    v_emp scott.emp%rowtype;

  5  begin

  6    open cur_emp;

  7    fetch cur_emp

  8      into v_emp;

  9    DBMS_output.put_line('empno: ' || v_emp.empno || ' ename: ' ||

 10                         v_emp.ename);

 11    close cur_emp;

 12  end;

 13  /

 

PL/SQL procedure successfully completed

 

或者也可以使用for loop 的形式來開啟和使用遊標:

SQL> declare

  2    cursor cur_emp is

  3      select * from scott.emp where rownum = 1;

  4    v_emp scott.emp%rowtype;

  5  begin

  6    for cs in cur_emp loop

  7        dbms_output.put_line('cs.empno:'||cs.empno);

  8    end loop;

  9  end;

 10  /

 

PL/SQL procedure successfully completed

 

也可以巢狀定義遊標

SQL> declare

  2    cursor cur_emp is

  3      select * from scott.emp a where a.empno = 7369;

  4 

  5    cursor cur_emp_1 is

  6      select cursor (select * from scott.emp a where a.empno = 7369)

  7        from scott.emp a where rownum <4;

  8    cur_type sys_refcursor;

  9    v_emp    scott.emp%rowtype;

 10 

 11  begin

 12    open cur_emp_1;

 13 

 14    loop

 15      fetch cur_emp_1

 16        into cur_type;

 17      exit when cur_emp_1%notfound;

 18      dbms_output.put_line('begin outer loop:');

 19      loop

 20        fetch cur_type

 21          into v_emp;

 22        exit when cur_type%notfound;

 23        dbms_output.put_line('begin inner loop:');

 24        dbms_output.put_line(v_emp.empno);

 25      end loop;

 26    end loop;

 27    close cur_emp_1;

 28  end;

 29  /

 

begin outer loop:

begin inner loop:

7369

begin outer loop:

begin inner loop:

7369

begin outer loop:

begin inner loop:

7369

 

PL/SQL procedure successfully completed

 

遊標屬性

注意到上述的

exit when cur_emp_1%notfound;

這裡是迴圈退出的條件, notfound是遊標的屬性之一,plsql提供了幾個遊標屬性:

名稱

描述

%FOUND

記錄成功提取,返回true,否則返回false

%NOTFOUND

記錄沒有成功提取,返回true,否則返回false

%ROWCOUNT

當前從遊標中提取的記錄數量

%ISOPEN

遊標是否已經開啟

%BULK_ROWCOUNT

返回由forall修改了的記錄數量

%BULK_EXCEPTIONS

返回forall語句修改後的行的例外資訊

%BULK_ROWCOUNT%BULK_EXCEPTIONS這兩個屬性在批量處理中示意。

 

 

 

隱式遊標

Plsql在每次執行一個DML語句(insert,update,delete)或者使用select into從資料庫中直接返回單行到plsql資料庫結構中是,都會定義和操縱一個隱式遊標。

之所以稱做隱式,是因為oracle自動的管理遊標相關的操作,比如分配一個遊標,開啟一個遊標,

提取記錄,乃至關閉遊標。例如:

SQL> declare

  2    v_emp scott.emp%rowtype;

  3    i     number := 0;

  4  begin

  5    for cur_emp in (select * from scott.emp where rownum = 1) loop

  6      DBMS_output.put_line(i);

  7    end loop;

  8  end;

  9  /

 

PL/SQL procedure successfully completed

 

遊標變數

可以在資料庫中宣告一個遊標的引用變數,可以在執行時賦值或者改變。

同時可以將遊標變數作為函式或者過程的引數,當需要傳遞結果集到其他環境(比較java)的時候,這種用法很有用。

宣告的遊標變數可以定義返回型別(強型別)或者不定義返回型別(弱型別),示例:

SQL> declare

  2    v_emp scott.emp%rowtype;

  3    type t_cur_emp_str is ref cursor return scott.emp%rowtype;

  4    v_cur_emp_str t_cur_emp_str;

  5    type t_cur_emp_weak is ref cursor;

  6    v_cur_emp_weak t_cur_emp_weak;

  7 

  8  begin

  9    open v_cur_emp_str for

 10      select * from scott.emp where rownum = 1;

 11 

 12    fetch v_cur_emp_str

 13      into v_emp;

 14 

 15    close v_cur_emp_str;

 16 

 17    open v_cur_emp_weak for

 18      select * from scott.emp where rownum = 1;

 19 

 20    fetch v_cur_emp_weak

 21      into v_emp;

 22    close v_cur_emp_weak;

 23  end;

 24  /

 

PL/SQL procedure successfully completed

 

 

或者用來作為函式或者儲存過程的引數

SQL> declare

  2    type tp_cur_emp_str is ref cursor return scott.emp%rowtype;

  3    cur_emp_str tp_cur_emp_str;

  4    v_emp_row   scott.emp%rowtype;

  5 

  6    function func_use_cur(cur in tp_cur_emp_str) return number is

  7      v_emprow scott.emp%rowtype;

  8      num      number := 0;

  9    begin

 10      loop

 11        fetch cur

 12          into v_emprow;

 13        exit when cur%notfound;

 14        num := cur%rowcount;

 15      end loop;

 16      return num;

 17    end;

 18 

 19    procedure proc_use_cur(cur in tp_cur_emp_str) is

 20      v_emprow scott.emp%rowtype;

 21    begin

 22      loop

 23        fetch cur

 24          into v_emprow;

 25        exit when cur%notfound;

 26        dbms_output.put_line('cur%rowcount :' || cur%rowcount);

 27        dbms_output.put_line('v_emprow.empno:' || v_emprow.empno);

 28      end loop;

 29    end;

 30 

 31  begin

 32    open cur_emp_str for

 33      select * from scott.emp a where rownum < 5;

 34    dbms_output.put_line('func_use_cur(cur_emp_str): ' ||

 35                         func_use_cur(cur_emp_str)||chr(10));

 36    close cur_emp_str;

 37 

 38    open cur_emp_str for

 39      select * from scott.emp a where rownum < 5;

 40    proc_use_cur(cur_emp_str);

 41    close cur_emp_str;

 42 

 43  end;

 44  /

 

func_use_cur(cur_emp_str): 4

 

cur%rowcount :1

v_emprow.empno:1

cur%rowcount :2

v_emprow.empno:7369

cur%rowcount :3

v_emprow.empno:7499

cur%rowcount :4

v_emprow.empno:7521

 

PL/SQL procedure successfully completed

 

也可以用作函式的返回值,或者函式和儲存過程的out 引數,或者結合管道函式方面的使用,在過程和函式小結時再具體舉例。

 

可以使用 where current of更新遊標中當前行如:

SQL> declare

  2    cursor cs is

  3      select * from scott.emp a where a.empno = 1 for update;

  4  begin

  5    for css in cs loop

  6      update scott.emp a set a.ename = 'ename' where current of cs;

  7    end loop;

  8  end;

  9  /

 

PL/SQL procedure successfully completed

 

再來看看使用顯式遊標與select into的隱式遊標在讀取一行時的比較,可以看出單行讀取的情況下好像隱式遊標稍稍好一點:

SQL> set timi on;

SQL> declare

  2    cursor cs is

  3      select dummy from dual;

  4    v_dummy dual.dummy%type;

  5  begin

  6    for i in 1 .. 100000 loop

  7      open cs;

  8      fetch cs

  9        into v_dummy;

 10      close cs;

 11    end loop;

 12  end;

 13  /

 

PL/SQL procedure successfully completed

 

Executed in 4.141 seconds

 

SQL>

SQL> declare

  2    v_dummy dual.dummy%type;

  3  begin

  4    for i in 1 .. 100000 loop

  5      select dummy into v_dummy from dual;

  6    end loop;

  7  end;

  8  /

 

PL/SQL procedure successfully completed

 

Executed in 3.172 seconds

很多時候把操作儘可能的讓oracle來做效果要好一點。

如果上面一個不明顯的話,再看一個含有order bycursor的例子:

create table t_test_cusor as select * from all_objects a ;

/

 

SQL> select count(*) from all_objects a ;

 

  COUNT(*)

----------

     59069

 

Executed in 3.265 seconds

 

SQL> declare

  2    cursor cs is

  3      select object_name from t_test_cusor a order by object_id desc;

  4    v_object_name t_test_cusor.object_name%type;

  5  begin

  6    for i in 1 .. 500 loop

  7      open cs;

  8      fetch cs

  9        into v_object_name;

 10      close cs;

 11    end loop;

 12  end;

 13  /

 

PL/SQL procedure successfully completed

 

Executed in 19.954 seconds

 

SQL> declare

  2    v_object_name t_test_cusor.object_name%type;

  3  begin

  4    for i in 1 .. 500 loop

  5      select object_name

  6        into v_object_name

  7        from (select object_name from t_test_cusor order by object_id desc)

  8       where rownum = 1;

  9    end loop;

 10  end;

 11  /

 

PL/SQL procedure successfully completed

 

Executed in 11.532 seconds

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16179598/viewspace-664455/,如需轉載,請註明出處,否則將追究法律責任。

相關文章