在 PL/SQL 程序中,对于处理多行记录的事务经常使用游标来实现。
为了处理 SQL 语句,ORACLE 必须分配一片叫上下文( context area )的区域来处理所必需的信息,其中包括要处理的行的数目,一个指向语句被分析以后的表示形式的指针以及查询的活动集(active set)。
游标是一个指向上下文的句柄( handle)或指针。通过游标,PL/SQL 可以控制上下文区和处理语句时上下文区会发生些什么事情。
类型 | 应用场景 |
---|---|
隐式游标 | 在PL/SQL程序中执行DML SQL语句或处理单行查询 语句 |
显式游标 | 用于处理返回单行或多行的查询 |
REF游标 | 用于处理运行时才能确定的动态SQL查询的结果 |
函数 | 属性 | 作用 |
---|---|---|
%found | 布尔型属性 | 如果游标指向的数据不为空,那么返回true,否则返回false |
%notfound | 布尔型属性 | 和 %found相反 |
%isopen | 布尔型属性 | 判断当前游标是否打开,如果是打开的返回true,否则返回false |
%rowcount | 数字型属性 | 表示游标执行的缓冲区(结果集)的数据条数 |
注:boolean 布尔类型 只有三个值: true、false、null
就是定义一个游标名,以及与其相对应的 select 语句.定义的游标不能有 into 子句。
cursor 游标名 is select语句;
就是执行游标所对应的 select 语句,将其查询结果放入工作区,并且指针指向工作区的首部,标识游标结果集合。如果游标查询语句中带有 for update 选项,open 语句还将锁定数据库表中游标结果集合对应的数据行。
open 游标名称;
在向游标传递参数时,可以使用与函数参数相同的传值方法,即位置表示法和名称表示法。PL/SQL 程序不能用 OPEN 语句重复打开一个游标。
fetch into 当前游标指向下一条数据并把数据保存在一个变量中
fetch 游标变量 into 变量;
当提取和处理完游标结果集合数据后,应及时关闭游标,以释放该游标所占用的系统资源,并使该游标的工作区变成无效,不能再使用 FETCH 语句取其中数据。关闭后的游标可以使用 OPEN 语句重新打开。
close 游标名称;
如:使用游标打印所有员工的信息
declare
--声明一个游标变量,指向所有员工的信息
cursor c_emp is select * from emp;
--声明一个emp%rowtype类型的变量用来保存游标中的一条记录
v_emp emp%rowtype;
begin
--打开游标
open c_emp;
--遍历游标
loop
--fetch..into 游标指向下一条数据,并保存数据到变量v_emp中
fetch c_emp into v_emp;
--判断游标是否还有数据,如果没有数据就跳出循环
exit when c_emp%notfound;
--把这条员工信息打印出来
dbms_output.put_line(v_emp.empno||','||v_emp.ename||','||
v_emp.job||','||v_emp.sal||','||v_emp.deptno);
end loop;
--关闭游标
close c_emp;
end;
declare
--声明一个游标变量指向所有员工的集合
cursor c_emp is select * from emp;
--声明一个变量用来保存一个游标的一条记录
v_emp emp%rowtype;
begin
--打开游标
open c_emp;
--执行一次fetch into 让游标指向第一条记录
fetch c_emp into v_emp;
--while循环,条件是循环条件
while c_emp%found loop
--打印员工信息
dbms_output.put_line(v_emp.ename||','||v_emp.job
||','||v_emp.sal||','||v_emp.deptno);
--fetch into 将游标指向下一条数据
fetch c_emp into v_emp;
end loop;
dbms_output.put_line(c_emp%rowcount);
close c_emp;
end;
声明显式游标时可以带参数以提高灵活性,语法:
cursor 游标名(参数名 数据类型,参数名 数据类型…) is select语句;
此时打开游标需要使用:open 游标名(参数,参数…)。另外,在指定数据类型时,不能使用长度约束。如 NUMBER(4)、CHAR(10) 等都是错误的。
如:
declare
--声明一个游标,输入一个部门号,打印该部门的员工信息
cursor c_emp(v_dno emp.deptno%type)is select * from emp where deptno=v_dno;
--声明一个rowtype类型变量保存游标中的一条记录
v_emp emp%rowtype;
begin
--打开游标
open c_emp(10);
loop
fetch c_emp into v_emp;
--判断游标当前数据是否为空,如果为空退出循环
exit when c_emp%notfound;
--打印员工信息
dbms_output.put_line(v_emp.ename||','||v_emp.job
||','||v_emp.sal||','||v_emp.deptno);
end loop;
close c_emp;
end;
游标修改和删除操作是指在游标定位下,修改或删除表中指定的数据行。这时,要求游标查询语句中必须使用 FOR UPDATE 选项,以便在打开游标时锁定游标结果集合在表中对应数据行的所有列和部分列。
为了对正在处理(查询)的行不被另外的用户改动,ORACLE 提供一个 FOR UPDATE 子句来对所选择的行进行锁住。该需求迫使 ORACLE 锁定游标结果集合的行,可以防止其他事务处理更新或删除相同的行,直到您的事务处理提交或回退为止。语法:
select…from…for update [nowait];
如果另一个会话已对活动集中的行加了锁,那么 SELECT FOR UPDATE 操作一直等待到其它的会话释放这些锁后才继续自己的操作,对于这种情况,当加上 NOWAIT 子句时,如果这些行真的被另一个会话锁定,则 OPEN 立即返回并给出:
ORA-0054 :resource busy and acquire with nowait specified
如果使用 FOR UPDATE 声明游标,则可在 DELETE 和 UPDATE 语句中使用 WHERE CURRENT OF cursor_name 子句,修改或删除游标结果集合当前行对应的数据库表中的数据行。
如:
declare
v_emp emp%rowtype;
cursor mycursor is select * from emp where empno=7369 for update;
begin
open mycursor;
fetch mycursor into v_emp;
while mycursor%found loop
--where current of mycursor 对游标读取的所在行进行更新或删除
update emp set ename='tom' where current of mycursor;
fetch mycursor into v_emp;
end loop;
close mycursor;
end;
在PL/SQL中使用DML语句时会自动创建隐式游标,隐式游标自动声明、打开和关闭,其名为 sql 。通过检查隐式游标的属性可以获取最近执行的DML语句的信息。
如:
declare
v_emp emp%rowtype;
begin
update emp set comm=100 where deptno=&deptno;
dbms_output.put_line('修改的数据条数:'||sql%rowcount);
if sql%found then
dbms_output.put_line('aaaaaaaaaaaaaa');
else dbms_output.put_line('没有该部门');
end if;
delete from emp where deptno=&dno;
dbms_output.put_line('删除了'||sql%rowcount||'条数据');
if sql%notfound then
dbms_output.put_line('没有该部门2');
end if;
end;
select…into 语句触发no_data_found;
当一个显式游标的where子句未找到时触发%notfound;
当update或delete语句的where子句未找到时触发 sql%notfound ,在提取循环中要用%notfound或 %found来确定循环的退出条件,不要用 no_data_found。
for循环会自动打开游标,关闭游标,并且自动会将游标指向一条记录
for后面不仅可以跟变量,也可以跟记录变量。当跟记录变量时,其结构与游标查询语句返回的结构集合的结构相同。在程序中可以通过引用该索引记录变量元素来读取所提取的游标数据,
记录变量中各元素的名称与游标查询语句选择列表中所制定的列名相同。如果在游标查询语句的选择列表中存在计算列,则必须为这些计算列指定别名后才能通过游标 FOR 循环语句中的索引变量来访问这些列数据。
如:
--使用游标打开所有员工的信息
declare
--声明一个游标变量指向所有员工的集合
cursor c_emp is select * from emp;
begin
for v_emp in c_emp loop
--打印员工信息
dbms_output.put_line(v_emp.ename||','||v_emp.job
||','||v_emp.sal||','||v_emp.deptno);
end loop;
end;
REF游标和游标变量用于处理运行时动态执行的SQL语句。创建游标变量需要两个步骤:
1、声明REF游标类型。语法:
type 类型名称 is ref cursor;
2、声明REF游标类型的变量。语法:
变量名称 游标变量类型;
使用:open 游标变量 for select语句
如:
--查询所有的员工信息
declare
--声明REF游标类型
type c_type is ref cursor;
--声明REF游标类型变量
cur c_type;
--声明一个变量保存emp表的信息
v_emp emp%rowtype;
begin
open cur for select * from emp;
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line(v_emp.ename);
end loop;
close cur;
end;