1. 程式人生 > >plsql遊標詳解——遊標中帶引數和引數遊標不是一回事

plsql遊標詳解——遊標中帶引數和引數遊標不是一回事


剛開啟遊標的時候,是位於一個空行,要用fetch into 才能到第一行。


只是要注意用更新遊標的時候,不能在遊標期間commit. 否則會報
ORA-01002: fetch out of sequence
      就是COMMIT;導致錯誤
       在開啟有for update的cursor時,系統會給取出的資料加上排他鎖(exclusive),
       這樣在這個鎖釋放前其他使用者不能對這些記錄作update、delete和加鎖。
       而我一旦執行了commit,鎖就釋放了,遊標也變成無效的,再去fetch資料時就出現錯誤了。
       因而要把commit放在迴圈外,等到所有資料處理完成後再commit,然後關閉cursor


隱含遊標
--------
又名SQL遊標,用於處理單行select into 和 DML語句。
SQL%ISOPEN SQL%FOUND SQL%NOTFOUND SQL%ROWCOUNT


顯示遊標
--------
使用者處理select語句返回的多行資料。
select語句返回多行資料處理方式:[1]顯示遊標;[2]select ... bulk collect into 集合變數...;


【1】顯示遊標屬性


[1] %ISOPEN 檢測遊標是否開啟。
[2] %FOUND 檢測遊標結果集是否存在資料。
[3] %NOTFOUND 是否不存在資料。
[4] %ROWCOUNT 遊標已提取的實際行數。


【2】使用顯示遊標


[1] 定義遊標


CURSOR cursor_name IS select_statement;


[2] 開啟遊標


OPEN cursor_name;


[3] 提取資料


FECTH cursor_name INTO variable,...;


[4] 關閉資料


CLOSE cursor_name;


使用標量變數:
-------------

DECLARE
Cursor emp_cur IS select ename,sal from emp order by empno;
v_ename emp.ename%TYPE;
v_sal   emp.sal%TYPE;
BEGIN
IF NOT emp_cur%ISOPEN THEN
   OPEN emp_cur; 
   DBMS_OUTPUT.PUT_LINE('開啟遊標');
END IF;
NULL;
LOOP
   FETCH emp_cur INTO v_ename,v_sal;
   EXIT WHEN emp_cur%NOTFOUND;
   DBMS_OUTPUT.PUT_LINE('使用者名稱:'||v_ename||',工資:'||v_sal);
END LOOP;
NULL;
IF emp_cur%ISOPEN THEN
   CLOSE emp_cur;
   DBMS_OUTPUT.PUT_LINE('關閉遊標');
END IF;
END;

使用PLSQL記錄變數
-----------------
DECLARE
Cursor emp_cur IS select ename,sal from emp order by empno;
emp_record emp_cur%ROWTYPE;
BEGIN
IF NOT emp_cur%ISOPEN THEN
   OPEN emp_cur; 
   DBMS_OUTPUT.PUT_LINE('開啟遊標');
END IF;
NULL;
LOOP
   FETCH emp_cur INTO emp_record;
   EXIT WHEN emp_cur%NOTFOUND;
   DBMS_OUTPUT.PUT_LINE('使用者名稱:'||emp_record.ename||',工資:'||emp_record.sal);
END LOOP;
NULL;
IF emp_cur%ISOPEN THEN
   CLOSE emp_cur;
   DBMS_OUTPUT.PUT_LINE('關閉遊標');
END IF;
END;

使用PLSQL集合變數
-----------------
DECLARE
Cursor emp_cur IS select ename,sal from emp order by empno;
TYPE emp_table_type IS TABLE OF emp_cur%ROWTYPE INDEX BY BINARY_INTEGER;
emp_table emp_table_type;
i number;
BEGIN
i := 1;
IF NOT emp_cur%ISOPEN THEN
   OPEN emp_cur; 
   DBMS_OUTPUT.PUT_LINE('開啟遊標');
END IF;
NULL;
LOOP
   FETCH emp_cur INTO emp_table(i);
   EXIT WHEN emp_cur%NOTFOUND;
   DBMS_OUTPUT.PUT_LINE('使用者名稱:'||emp_table(i).ename||',工資:'||emp_table(i).sal);
   i := i + 1;
END LOOP;
NULL;
IF emp_cur%ISOPEN THEN
   CLOSE emp_cur;
   DBMS_OUTPUT.PUT_LINE('關閉遊標');
END IF;
END;

【3】循環遊標


FOR record_name IN cursor_name|select_statement LOOP
   statement;
   ....
END LOOP;


使用循環遊標
------------
declare 
cursor emp_cusor is select ename,sal from emp where deptno = &no order by empno;
begin
for emp_record in emp_cusor loop
      dbms_output.put_line('姓名:'||emp_record.ename||',工資:'||emp_record.sal);
end loop;
end;

begin
for emp_record in (select ename,sal from scott.emp where deptno = &no order by empno) loop
      dbms_output.put_line('姓名:'||emp_record.ename||',工資:'||emp_record.sal);
end loop;
end;

【4】引數遊標


CURSOR cursor_name(parameter_name datatype) IS select_statement; --只能制定型別,不能指定具體大小
OPEN cursor_name(引數值);
FECTH cursor_name INTO variable,...;
CLOSE cursor_name;


使用遊標引數
------------



示例1:
------

declare 
cursor emp_cursor(v_depnto number) is select ename,sal from scott.emp where deptno = v_depnto order by empno;
emp_record emp_cursor%rowtype;
v_dno number;
begin
v_dno := &no;
if not emp_cursor%isopen then
      open emp_cursor(v_dno);
end if; 
null;
loop
        fetch emp_cursor into emp_record; 
        exit when emp_cursor%notfound;
        dbms_output.put_line('姓名:'||emp_record.ename||',工資:'||emp_record.sal);
end loop;
null;
if emp_cursor%isopen then
       close emp_cursor;
end if;
end;
示例2
-----
declare 
cursor emp_cursor(v_depnto number) is select ename,sal from scott.emp where deptno = v_depnto order by empno;
v_dno number;
begin
v_dno := &no;
for emp_record in emp_cursor(v_dno) loop
      dbms_output.put_line('姓名:'||emp_record.ename||',工資:'||emp_record.sal);
end loop;
end;

【5】更新、刪除遊標行
CURSOR cursor_name IS select_statement
FOR UPDATE [OF column_reference] [NOWAITE];   -- OF子句指定對特定表加鎖。
UPDATE table_name SET column=.. WHERE CURRENT OF cursor_name;
DELETE table_name WHERE CURRENT OF cursor_name;

使用遊標更新資料
----------------
declare
       cursor test_cursor is select empno,ename,sal,deptno from scott.test for update;
       test_record test_cursor%rowtype;
       v_deptno number;
       v_sal test.sal%type;
begin
       v_deptno := &no;
       if not test_cursor%isopen then
          open test_cursor;
       end if;
       loop
          fetch test_cursor into test_record;
          exit when test_cursor%notfound;
          dbms_output.put_line('姓名:'||test_record.ename||',舊工資:'||test_record.sal);
          if test_record.deptno = v_deptno then
             update scott.test set sal=2*sal where current of test_cursor;
          else
             update scott.test set sal=3*sal where current of test_cursor;
          end if;
       end loop;
       close test_cursor;
end;
       
declare
cursor test_cursor is select empno,ename,sal,deptno from scott.test for update;
      v_deptno NUMBER:=&dno;
begin
for test_record in test_cursor loop
        if test_record.deptno = v_deptno then
                 dbms_output.put_line('姓名:'||test_record.ename||',舊工資:'||test_record.sal);
                 update scott.test set sal = sal*1.5 where current of test_cursor;
           end if;
       end loop;
end;


表test換成emp出現ORA-01410: 無效的 ROWID錯誤,什麼原因???


使用遊標刪除資料
----------------
declare 
cursor test_cursor(v_deptno number) is select deptno,empno,ename,comm from scott.test where deptno = v_deptno for update;
v_dno test.deptno%type := &dno;
begin
     for test_record in test_cursor(v_dno) loop
         if test_record.comm is null then
            dbms_output.put_line('使用者名稱:'||test_record.ename||'部門:'||test_record.deptno);
            delete from scott.test where current of test_cursor;
         end if;
     end loop;     
end;
【6】遊標變數


指向記憶體地址的指標。可以在開啟遊標時指定其所對應的SELECT語句,實現動態遊標。


[1]定義REF CURSOR型別和遊標變數:
Type ref_type_name IS REF CURSOR [RETURN return_type --必須是PL/SQL記錄型別]; 
說明:如果指定RETURN子句,那麼在開啟遊標時SELECT語句的返回結果必須與RETURN子句所指定的記錄型別匹配。
cursor_variable ref_type name;


SYS_REFCURSOR


[2]開啟遊標,指定對應的SELECT語句:
OPEN cursor_variable FOR select_statement;
[3]提取資料
FETCH cursor_variable INTO variable1,variable,...;
[4]關閉遊標
CLOSE cursor_variable;


不使用RETURN子句
----------------
DECLARE
TYPE ref_type_table IS REF CURSOR;
v_cursor            ref_type_table;
emp_record          emp%rowtype;
BEGIN
     OPEN v_cursor FOR select * from emp where deptno=&no;
     LOOP
         FETCH v_cursor INTO emp_record;
         EXIT WHEN v_cursor%NOTFOUND;
         dbms_output.put_line('員工號:'||emp_record.ename||'部門號:'||emp_record.deptno);
     END LOOP;
     CLOSE v_cursor;
END;

使用RETURN子句
--------------
DECLARE
emp_record          emp%rowtype;
TYPE ref_type_table IS REF CURSOR RETURN emp%rowtype;
v_cursor            ref_type_table;
BEGIN
     OPEN v_cursor FOR select * from emp where deptno=&no;
     LOOP
         FETCH v_cursor INTO emp_record;
         EXIT WHEN v_cursor%NOTFOUND;
         dbms_output.put_line('員工號:'||emp_record.ename||'部門號:'||emp_record.deptno);
     END LOOP;
     CLOSE v_cursor;
END;

DECLARE
Type emp_record_type IS RECORD(
       ename emp.ename%TYPE,
       salary emp.sal%TYPE,
       deptno emp.deptno%TYPE);
emp_record emp_record_type;

TYPE ref_type_table IS REF CURSOR RETURN emp_record_type;
v_cursor            ref_type_table;
BEGIN
     OPEN v_cursor FOR select ename,sal,deptno from emp where deptno=&no;
     LOOP
         FETCH v_cursor INTO emp_record;
         EXIT WHEN v_cursor%NOTFOUND;
         dbms_output.put_line('員工號:'||emp_record.ename||',部門號:'||emp_record.deptno||',工資:'||emp_record.salary);
     END LOOP;
     CLOSE v_cursor;
END;

【7】使用遊標批量獲取


FETCH ... BULK COLLECT INTO ...[LIMIT row_number];


不限制行數
----------
DECLARE
     CURSOR emp_cursor(v_deptno number) IS SELECT * FROM EMP WHERE deptno = v_deptno;
     TYPE type_emp_table IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;
     emp_table type_emp_table;
     v_dno emp.deptno%TYPE;
BEGIN
     v_dno := &no;
     OPEN emp_cursor(v_dno);
     FETCH emp_cursor BULK COLLECT INTO emp_table;
     CLOSE emp_cursor;
     FOR i IN 1..emp_table.COUNT LOOP
         dbms_output.put_line('員工號:'||emp_table(i).ename||'工資:'||emp_table(i).sal);
     END LOOP;
     CLOSE emp_cursor;
END;


限制行數
--------
DECLARE
     CURSOR emp_cursor(v_deptno number) IS SELECT * FROM EMP WHERE deptno = v_deptno;
     TYPE type_emp_table IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;
     emp_table type_emp_table;
     v_dno emp.deptno%TYPE;
BEGIN
     v_dno := &no;
     OPEN emp_cursor(v_dno);
     FETCH emp_cursor BULK COLLECT INTO emp_table LIMIT &2;
     CLOSE emp_cursor;
     FOR i IN 1..emp_table.COUNT LOOP
         dbms_output.put_line('員工號:'||emp_table(i).ename||'工資:'||emp_table(i).sal);
     END LOOP;
     CLOSE emp_cursor;
END;




【8】使用CURSOR表示式


作用巢狀遊標。


DECLARE
       CURSOR dept_emp_cursor(v_deptno number) IS 
   SELECT dname,cursor(SELECT * FROM emp e WHERE e.deptno = d.deptno) 
   FROM dept d WHERE deptno = v_deptno;
       TYPE emp_cursor_type IS REF CURSOR;
       emp_cursor emp_cursor_type;
       emp_record emp%ROWTYPE;
       v_name dept.dname%TYPE;
       v_dno emp.deptno%TYPE;
BEGIN
     v_dno := &no;
     OPEN dept_emp_cursor(v_dno);
     loop 
          FETCH dept_emp_cursor INTO v_name,emp_cursor;
          EXIT WHEN dept_emp_cursor%NOTFOUND;
          dbms_output.put_line('部門名稱:'||v_name);
          LOOP
              FETCH emp_cursor INTO emp_record;            
              EXIT WHEN emp_cursor%NOTFOUND;
              dbms_output.put_line('員工名稱:'||emp_record.ename||',工資:'||emp_record.sal);
          END LOOP;
     end loop;
     CLOSE dept_emp_cursor;
END;


轉自:http://hi.baidu.com/tangwei%5Fbd/blog/item/f4adbe02e578391b738b6533.html