PL/SQL游标使用

简介:

游标只是一个指向查询语句返回的结果的指针,因此在游标定义时将包含一个查询定义。当游标打开后,数据被接收到一块内存区域存储,直到游标关闭。

游标实际上指向的是一块内存区域,这块内存区域位于进程全局区内部,称为上下文区域,在上下文区域中包含如下3类信息

1.查询返回的数据行

2.查询所处理的数据的行号

3.指向共享池中的已分析的SQL语句

游标实际上指向一块内存区域

游标定义时并不会获取游标数据,只有在游标被打开后,游标相关的查询语句被执行,然后将检索到的结果保存到内存中。

游标结构示意图

游标指向PGA,上下文区

查询结果集

查询处理的行数

共享池中已分析的查询语句 


使用游标例子

declare

emprow emp%ROWTYPE

cursor emp_cur

is

select * from emp where deptno is not null;

begin

open emp_cur;

loop

fetch emp_cur

into emprow

dbms_output.put_line('员工编号:'

||emprow.empno

||' '

||'员工名称:'

||emprow.ename

);

exit when emp_cur%NOTFOUND;

end loop;

close emp_cur;

end;


游标分类

1.显式游标

2.隐式游标  也叫SQL游标

执行每一个DML操纵语句时,oracle都会在PGA中的一个上文区域中具有一个隐式的游标。

begin

update emp set comm = comm * 1.12 where empno = 7369;

dbms_output.put_line(SQL%ROWCOUNT || '行被更新');

if SQL%NOTFOUND

then

dbms_output.put_line('不能更新员工号为7369的员工!');

end if;

commit;

exception

when others

then

dbms_output.put_line(SQLERRM);

end;


定义游标类型

declare

cursor emp_cursor

is

select * from emp where deptno=20;

begin

null;

end;

不能把值赋给游标名或者在表达式中使用它,但是游标和变量有着同样的作用域规则


declare

v_deptno NUMBER;

cursor emp_cursor

is

select * from emp where deptno = v_deptno;

begin

v_deptno:=20;

open emp_cursor;

if emp_cursor%ISOPEN then

dbms_output.put_line('游标已经被打开');

end if;

end;

   v_deptno是绑定变量,该变量必须在游标声明前之前进行声明,否则oracle会报错。游标的形式参数都必须IN模式,并且不能给游标的参数添加NOT NULL约束

declare

cusor emp_cursor(p_deptno in number)

is

select * from emp where deptno=p_deptno;

begin

open emp_cursor(20);

end;

可以指定return子句定义游标返回值的类型,返回值的类型一定要和返回的结果集的类型一致,因此返回值一般为记录类型或%ROWTYPE指定的表中的类型

declare

cursor emp_cursor(p_deptno in number)return emp%ROWTYPE

is

select * from emp where deptno=p_deptno;

begin

open emp_cursor(20);

end;


使用游标属性

1.%ISOPEN

判断对应的游标变量是否打开,如果游标变量打开,则返回true,否则返回false。


declare

cursor emp_cursor(p_deptno in number)

is

select * from emp where deptno=p_deptno;

begin

if not emp_cursor%ISOPEN then

open emp_cursor(20);

end if;

if emp_cursor%ISOPEN then

dbms_output.put_line('游标已经打开了');

else

dbms_output.put_line('游标还没有被打开!');

end if;

close emp_cursor;

end;

2.%FOUND

检查是否从结果集中提取到了数据

declare

emp_row emp%ROWTYPE;

cursor emp_cursor(p_deptno in number)

is

select * from emp where deptno=p_deptno;

begin

if not emp_cursor%ISOPEN

then

open emp_cursor(20);

end if;

if emp_cursor%FOUND is null

then

dbms_output.put_line('%FOUND属性为NULL');

end if;

loop

fetch emp_cursor

into emp_row;

exit when not emp_cursor%FOUND;

end loop;

close emp_cursor;

end;


3.%NOTFOUND属性

该属性与%FOUND属性相反,当没有从游标中提取到数据时,该属性返回True,否则返回False,与%Found一样

declare

emp_row emp%ROWTYPE;

cursor emp_cursor(p_deptno in number)

is select * from emp where deptno=p_deptno;

begin

open emp_row(20);

if emp_cursor%NOTFOUND is null;

then

dbms_output.put_line('%NOTFOUND属性为null');

end if;

loop

fetch emp_cursor

into emp_row;

exit when emp_cursor%NOTFOUND;

end loop;

close emp_cursor;

end;

4.%ROWCOUNT

用来返回到目前为止已经从游标中取出的记录的行数

declare

emp_row emp%ROWTYPE;

cursor emp_cursor(p_deptno in number)

is

select * from emp where deptno=p_deptno;

begin

open emp_cursor(20);

loop

fetch emp_cursor

into emp_row;

exit when emp_cursor%NOTFOUND;

dbms_output.put_line('当前已提取的行数为:'||emp_cursor%ROWCOUNT ||'行!');

end loop;

close emp_cursor;

end;

5.提取游标数据

declare

deptno dept.deptno%TYPE;

dname dept.dname%TYPE;

loc dept.loc%TYPE;

dept_row dept%ROWTYPE;

cursor dept_cur is select * from dept;

begin

open dept_cur;

loop

if dept_cur%ROWCOUNT<=4 then

fetch dept_cur into dept_row;

if dept_cur%FOUND then

dbms_output.put_line(dept_row.deptno||' '|| dept_row.dname||' '||dept_row.loc);

end if;

else

fetch dept_cur into deptno,dname,loc;

if dept_cur%FOUND then

dbms_output.put_line(deptno||' '||dname||' '||loc);

end if;

end if;

exit when dept_cur%NOTCOUND;

end loop;

close dept_cur;

end;

批量提取数据

因为fetch语句一次只能提取一行,并且提取只能是向前的,因此如果要重新提取已经提取过的数据,只有重新打开游标

可以使用BULK COLLECT批处理子句可以一次性将游标中的结果集保存到集合中,这样就可以在集合中进行前进和后退处理。

declare

type depttab_type is table of dept%ROWTYPE;

depttab depttab_type;

cursor deptcur is select * from dept;

begin

open deptcur;

fetch deptcur BULK COLLECT INTO depttab;

for i IN 1 ..depttab.count

loop

dbms_output.put_line(depttab(i).deptno

||' '

||depttab(i).dname

||' '

||depttab(i).loc

);

end loop;

close deptcur;

end;


bulk collect into 会一次把所有数据都提取到集合中,如果数据量特别大,并且使用varray这样的具有固定元素个数的集合时,可能需要限制每次提取的行数,可以使用fetch bulk collect into limit语句提取部分数据。

declare

type dept_type is varray(4) of dept%ROWTYPE;

depttab dept_type

cursor dept_cursor

is

select * from dept;

v_rows int :=4;

v_count int :=0;

begin

open dept_cursor;

loop

fetch dept_cursor bulk collect into depttab limit v_rows;

exit when dept_cursor%NOTFOUND;

dbms_output.put('部门名称:');

for i in 1 .. (dept_cursor%ROWCOUNT-v_count)

loop

dbms_output.put(depttab(i).dname ||' ');

end loop;

dbms_output.new_line;

v_count:=dept_cursor%ROWCOUNT;

end loop;

close dept_cursor;

end;



操纵游标数据

1.loop循环

declare

dept_row dept%ROWTYPE

cursor dept_cursor is select * from dept;

begin

open dept_cursor;

loop

fetch dept_cursor into dept_row;

exit when dept_cursor%NOTFOUND;

dbms_output.put_line('部门名称:'||dept_row.dname);

end loop;

close dept_cursor;

end;

2.while循环

declare

dept_row dept%ROWTYPE;

cursor dept_cursor is select * from dept;

begin

open dept_cursor;

fetch dept_cursor into dept_row;

while dept_cursor%FOUND loop

dbms_output.put_line('部门名称:'||dept_row.dname);

fetch dept_cursor into dept_row;

end loop;

close dept_cursor;

end;

3.游标for循环

declare

cursor dept_cursor is select * from dept;

begin

for dept_row in dept_cursor loop

dbms_output.put_line('部门名称:'||dept_row.dname);

end loop;

end;


可以在需要传递参数时,可以直接在游标名后面加入参数值

for dept_row in dept_cursor(20) loop

可以直接在for语句的in子句中使用子查询,而不是显式的声明一个游标

begin

for dept_row in (select * from dept)loop

dbms_output.put_line('部门名称:'||dept_row.dname);

end loop;

end;


修改游标数据

1.在游标的声明部分添加for update子句

2.子句是在update或delete 语句 中添加where  current of 子句

1.for update

会对用select语句提取出来结果进行锁定,相当于给结果集的行加了一把互斥锁,实行行级锁定。这样其他的用户就不能对当前游标行进行修改或删除。

例子:定义select语句中使用了for update来锁定deptno 和dname这两个列。

cursor dept_cursor is select * from dept for update deptno,dname;


2.where current of

在使用for update语句锁定了表中的行后,可以在update或delete语句中使用where current of子句来得到当前游标所检索出来的行

where current of cursorname;

cursorname是当前使用for update子句的游标的名称,用来更新游标数据。

where cursor of 子句检索的游标一定要有for update子句,并且游标要被打开且至少返回一行,不然oracle会触发错误。


使用for update与where current of 子句来更新emp表中部门编号为20的员工提成

declare

cursor emp_cursor(p_deptno in number)

is

select * from emp where deptno=p_deptno for update;

begin

for emp_row in emp_cursor(20)

loop

update emp

set comm = comm * 1.12

where cursor of emp_cursor;

end loop;

commit;

end;

commit语句必须放在循环语句的后面,否则会导致游标更新或删除失败。



使用游标删除数据

declare

cursor emp_cursor(p_empno in number)

is

select * from emp where empno = p_empno for update;

begin

for emp_row in emp_cursor(7369)

loop

delete from emp where cursor of emp_cursor;

end loop;

end;


游标变量

在前面定义一个游标,就为其绑定一个查询语句,这种游标称为静态游标。游标变量是另一种类型的游标,在定义时并不绑定到具体的查询,而是可以打开任何类型兼容的查询,灵活性相当大。

在PL/SQL中,指针是使用REF作为前缀进行定义的,因此游标变量类型就是REF cursor类型。

游标变量示例

declare

type emp_type is ref cursor return emp%ROWTYPE;

emp_cur emp_type;

emp_row emp%ROWTYPE;

begin

open emp_cur for select * from emp;

loop

fetch emp_cur into emp_row;

exit when emp_cur%NOTFOUND;

dbms_output.put_line('员工名称:'||emp_row.ename);

end loop;

end;



静态游标与游标变量的一个区别是游标变量指向的是一个查询的工作区,而静态游标指向的是数据库中的一个命名的工作区。游标变量不依赖一个特定的工作区,这个工作区是动态的。当一个游标变量指向一个特定的工作区的时候,oracle会为它保留该存储空间。因此可以在运行时为游标变量赋值一个新的值,将它作为一个参数传递给本地和存储过程,使得子程序可以用一个方便的路径来集中检索数据。



声明游标变量类型

游标变量是一种引用类型,类似于C语言的指针。

REF表示为一个指针类型。

declare

type emp_type is ref cursor return emp%ROWTYPE;

type gen_type is ref cursor;

emp_type使用了return子句进行约束,又称为强类型的游标变量,任何使用这种类型的游标变量在使用fetch into 语句提取数据时,都必须要匹配return指定的数据结构。


gen_type没有使用return子句,又称为弱类型的游标变量,使用这种类型的游标变量没有与任何记录数据结构关联,使用这种类型的定义可以比强类型的游标变量提供更多的灵活性,使其可以用于任意的查询,匹配任意的记录类型。



定义游标变量

declare

type emp_type is ref cursor return emp%ROWTYPE;

type gen_type is ref cursor;

emp_cur emp_type;

gen_cur gen_type;

begin

open emp_cur for select * from emp where deptno=20;

end;

静态游标是一个指向具体结果集的常量,是一个具体的游标对象;而游标变量是一个指向游标对象的指针,它指向具体的游标对象

PGA             游标变量1           游标变量2

| |

|————————————

SGA 实际的游标对象 查询的结果


如果type语句中未指定return子句,则可以连续地打开多次,分别为其赋不同的查询select子句。

重新打开一个游标变量以前不需要关闭它,当用不同的查询语句打开同一个游标变量的时候,上一个查询将被丢弃掉。

declare

type emp_curtype is ref cursor;

emp_cur emp_curtype;

begin

open emp_cur for select * from emp;

open emp_cur for select empno from emp;

open emp_cur for select deptno from dept;

end;



处理游标变量异常

如何通过处理INVALID_CURSOR异常,将一个已经打开的游标变量赋给另一个未打开的游标变量

declare

type emp_curtype is ref cursor;

emp_cur1 emp_curtype;

emp_cur2 emp_curtype;

emp_row emp%ROWTYPE;

begin

open emp_cur1 for select * from emp where deptno=20;

fetch emp_cur1 into emp_row;

dbms_output.put_line('员工名称:'||emp_row.ename||'部门编号:'||emp_row.deptno);

fetch emp_cur2 into emp_row;

exception

when invalid_cursor then

emp_cur2:=emp_cur1;

fetch emp_cur2 into emp_row;

dbms_output.put_line('员工名称:'||emp_row.ename||'部门编号:'emp_row.deptno);

open emp_cur2 for select * from emp where deptno=30;

fetch emp_cur1 into emp_row;

dbms_output.put_line('员工名称:'||emp_row.ename||'部门编号:'||emp_row.deptno);

end;



另一个常见的异常类型rowtype_mismatch异常。

declare

type emp_curtype is ref cursor;

emp_cur emp_curtype;

emp_row emp%ROWTYPE;

dept_row dept%ROWTYPE;

begin

open emp_cur for select * from emp where deptno=20;

fetch emp_cur into dept_row;

exception

when ROWTYPE_MISMATCH then

fetch emp_cur into emp_row;

dbms_output.put_line('员工名称:'||emp_row.ename||'部门编号:'||emp_row.deptno);

end;

除了使用弱类型的游标变量定义方式,也可以使用SYS_REFCURSOR类型

declare

emp_cur SYS_REFCURSOR;

emp_row emp%ROWTYPE;

dept_row dept%ROWTYPE;

begin

open emp_cur for select * from emp where deptno=20;

fetch emp_cur into dept_row;

exception

when ROWTYPE_MISMATCH then

fetch emp_cur into emp_row;

dbms_output.put_line('员工名称:'||emp_row.ename||'部门编号:'||emp_row.deptno);

end;

 


在包中使用游标变量

create or replace package emp_data_action as

type emp_type is ref cursor return emp%ROWTYPE;

procedure getempbydeptno(emp_cur in out emp_type,p_deptno number);

end emp_data_action;


create or replace package body emp_data_action as procedure getembydeptno(emp_cur in out emp_type,p_deptno number) is emp_row emp%ROWTYPE;

begin

open emp_cur for select * from emp where deptno=p_deptno;

loop

fetch emp_cur into emp_row;

exit when emp_cur%NOTFOUND;

dbms_output.put_line('员工名称:'||emp_row.ename||'部门编号:'||emp_row.deptno);

end loop;

close emp_cur;

end;

end emp_data_action;


可以在包中声明游标类型,但是不能在包中声明游标变量

调用包中的过程:

declare

emp_cursors emp_data_action.emp_type;

begin

emp_data_action.getempbydeptno(emp_cursors,20);

end;


游标变量的限制

1.不能在包中声明游标变量

2.不能在创建表或创建视图的语句中把字段类型指定为ref cursor类型,数据库字段是不能存放游标变量值的

3.游标类型的参数不支持使用远程过程调用(RPC)将游标变量从一个服务器传递到另一个服务器。

4.不能用比较操作符来判断两个游标变量是否相等,不相等或者为NULL

5.不能为游标变量赋空值

6.不能将ref cursor 类型作为集合的元素类型,也就是说在索引表,嵌套表和变长数组中不能存放游标变量的值

7.不能将在游标中使用的游标for循环用在游标变量上,也就是说游标和游标变量不要试图互相替换。




      本文转自潘阔 51CTO博客,原文链接:http://blog.51cto.com/pankuo/1630252,如需转载请自行联系原作者





相关文章
|
6天前
|
SQL 存储 Oracle
Oracle的PL/SQL定义变量和常量:数据的稳定与灵动
【4月更文挑战第19天】在Oracle PL/SQL中,变量和常量扮演着数据存储的关键角色。变量是可变的“魔术盒”,用于存储程序运行时的动态数据,通过`DECLARE`定义,可在循环和条件判断中体现其灵活性。常量则是不可变的“固定牌”,一旦设定值便保持不变,用`CONSTANT`声明,提供程序稳定性和易维护性。通过 `%TYPE`、`NOT NULL`等特性,可以更高效地管理和控制变量与常量,提升代码质量。善用两者,能优化PL/SQL程序的结构和性能。
|
1月前
|
SQL Perl
PL/SQL经典练习
PL/SQL经典练习
13 0
|
1月前
|
SQL Perl
PL/SQL编程基本概念
PL/SQL编程基本概念
13 0
|
1月前
|
SQL Perl
PL/SQL Developer 注册机+汉化包+用户指南
PL/SQL Developer 注册机+汉化包+用户指南
16 0
|
6天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL游标属性:数据的“导航仪”与“仪表盘”
【4月更文挑战第19天】Oracle PL/SQL游标属性如同车辆的导航仪和仪表盘,提供丰富信息和控制。 `%FOUND`和`%NOTFOUND`指示数据读取状态,`%ROWCOUNT`记录处理行数,`%ISOPEN`显示游标状态。还有`%BULK_ROWCOUNT`和`%BULK_EXCEPTIONS`增强处理灵活性。通过实例展示了如何在数据处理中利用这些属性监控和控制流程,提高效率和准确性。掌握游标属性是提升数据处理能力的关键。
|
6天前
|
SQL Oracle 安全
Oracle的PL/SQL循环语句:数据的“旋转木马”与“无限之旅”
【4月更文挑战第19天】Oracle PL/SQL中的循环语句(LOOP、EXIT WHEN、FOR、WHILE)是处理数据的关键工具,用于批量操作、报表生成和复杂业务逻辑。LOOP提供无限循环,可通过EXIT WHEN设定退出条件;FOR循环适用于固定次数迭代,WHILE循环基于条件判断执行。有效使用循环能提高效率,但需注意避免无限循环和优化大数据处理性能。掌握循环语句,将使数据处理更加高效和便捷。
|
6天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL条件控制:数据的“红绿灯”与“分岔路”
【4月更文挑战第19天】在Oracle PL/SQL中,IF语句与CASE语句扮演着数据流程控制的关键角色。IF语句如红绿灯,依据条件决定程序执行路径;ELSE和ELSIF提供多分支逻辑。CASE语句则是分岔路,按表达式值选择执行路径。这些条件控制语句在数据验证、错误处理和业务逻辑中不可或缺,通过巧妙运用能实现高效程序逻辑,保障数据正确流转,支持企业业务发展。理解并熟练掌握这些语句的使用是成为合格数据管理员的重要一环。
|
6天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL表达式:数据的魔法公式
【4月更文挑战第19天】探索Oracle PL/SQL表达式,体验数据的魔法公式。表达式结合常量、变量、运算符和函数,用于数据运算与转换。算术运算符处理数值计算,比较运算符执行数据比较,内置函数如TO_CHAR、ROUND和SUBSTR提供多样化操作。条件表达式如CASE和NULLIF实现灵活逻辑判断。广泛应用于SQL查询和PL/SQL程序,助你驾驭数据,揭示其背后的规律与秘密,成为数据魔法师。
|
1月前
|
SQL Oracle 关系型数据库
Oracle系列十一:PL/SQL
Oracle系列十一:PL/SQL