您的位置:首页 > 数据库

PL/SQL游标详解

2013-09-25 13:46 323 查看
刚打开游标的时候,是位于一个空行,要用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;

使用标量变量:
-------------

[sql] view plaincopy

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记录变量
-----------------

[sql] view plaincopy

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集合变量
-----------------

[sql] view plaincopy

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;

使用循环游标
------------

[sql] view plaincopy

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:
------

[sql] view plaincopy

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
-----

[sql] view plaincopy

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】更新、删除游标行

[sql] view plaincopy

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;

使用游标更新数据
----------------

[sql] view plaincopy

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错误,什么原因???

使用游标删除数据
----------------

[sql] view plaincopy

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子句
----------------

[sql] view plaincopy

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子句
--------------

[sql] view plaincopy

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];

不限制行数
----------

[sql] view plaincopy

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;

限制行数
--------

[sql] view plaincopy

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表达式

作用嵌套游标。

[sql] view plaincopy

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: