1、CURSOR综述
l cursor是用于提取多行数据集
l 游标的使用
2、申明游标
l 申明语法
declare
CURSER 游标名 IS 查询语句
注:如果在查询语句中使用了pl/sql变量,变量声明必须放在游标的前面
DECLARE
v_major studengs.major%type;
CURSOR c_student IS
SELECT first_name,last_name
FROM students
WHERE major = v_major
l 变量名与列名类型要一致
l 游标必须在PL/SQL快的声明部分定义
l 定义游标时并没有生成数据,只是将定义信息保存到数据字典中;
l 游标定义后,可以使用 游标名%ROWTYPE定义游标类型变量
3、打开游标 OPEN 游标名
PS:当打开游标的时候,就代表所定义的游标后面的查询语句真正的被执行,在打开游标的的时候变量一定要去初始化值,否则这个值就是一个空值,导致查询后面的where条件就失去了意义。最后把执行的查询结果返回到游标所定义的空间当中,并且在游标当中会提供一些操作方法来操作这些语句
注:如果游标对应的select语句使用了“FOR UPDATE”(行级排他锁)选项,则此游标锁定
说明:
l 检查变量的值
l 执行游标定义时对应的select语句将查询结果检索到工作区中
l 游标指针指向第一个元素
l 一旦游标打开,就无法再次打开,除非先关闭
l 如果游标定义中的变量值发生变化,则只能在下次打开游标时才起作用
4、检索游标 FETCH FROM CURSOR
l 从游标中提取数据,fetch的两种形式
Ø fetch 游标名 INTO 变量1,变量2,……;
Ø fetch 游标名 INTO Record_var 或者是%ROWTYPE类型变量
Ø 注:每fetch一次,只能从游标中提取一行
l 说明
Ø 在使用fetch语句之前必须先打开游标
Ø 对游标第一次使用fetch时,游标指针指向第一条记录,因此操作的对象是第一条记录,使用后,游标指向下一条记录
Ø 游标指针只能向下移动,不能回退
Ø INTO子句中的变量个数,顺序,数据类型必须与游标工作区每行记录的字段数,顺序以及数据类型一一对应
5、关闭游标 close 游标名
l 游标使用后应该关闭【务必谨记】
fetch 关闭后的游标 是非法的
关闭一个关闭了的游标也是非法的
l 说明:
游标所对应的内存工作区变为无效,释放与游标相关的系统资源
declare
v_id number(4);
CURSOR my_cur IS --声明游标
select first_name,salary,start_date from s_emp where id = v_id;
v_fn s_emp.first_name%type; --定义三个变量用以接收游标里面查询出来的参数
v_sal s_emp.salary%type;
v_sd s_emp.start_date%type;
begin
v_id := &id; --根据输入的id号码来查询出所对应的结果值
open my_cur; --打开游标
fetch my_cur into v_fn,v_sal,v_sd; --检索游标
--打印输出结果
dbms_output.put_line('id:' || v_id || ' name:' || v_fn || ' salary:' || v_sal || ' start_date:' || v_sd );
close my_cur; --关闭游标(必须的)
end;
--查询到的结果是这样的:id:2 name:LaDoris salary:1600 start_date:08-5月 -90
6、游标的属性
l %FOUND 如果fetch到数据。则返回true,否则返回false
l %NOT FOUND 如果fetch不到数据,则返回true,否则返回false
l %ISOPEN 如果当前游标已打开,则返回true
l %ROWCOUNT 返回当前游标的指针位移量,当然,游标一定要处于打开状态
7、游标的fetch循环
l 采用LOOP循环
LOOP
FETCH 游标名 INTO …… --提取数据
EXIT WHEN 游标名%NOTFOUND;--退出条件
END LOOP --结束循环
close 游标名 --关闭游标
l 采用while循环
while 游标名%FOUND LOOP
fetch 游标名 INTO ……
END LOOP
close 游标名 –关闭游标
--注意:在使用while的时候,在while之前一定要先fetch一下先,否则进不了循环体,因为开始是空的
l 采用FOR循环
FOR var IN cursor LOOP
……
END LOOP
注:在for循环中,会自动打开,自动fetch,和自动关闭游标,所以,使用for循环是最为简便的方法
8、一个游标的具体实例:
利用LOOP或WHILE循环和游标来输出工资小于1500远的员工全名、职称,入职时间
方法一:分别定义变量和游标
declare
v_fName varchar2(128); --定义三个变量来存放数据
v_title s_emp.title%type;
v_startDate s_emp.start_date%type;
CURSOR my_cur IS --声明一个游标
select first_name|| ' '||last_name,title,start_date from s_emp where salary<1500;
begin
open my_cur;
loop
fetch my_cur into v_fName,v_title,v_startDate;
dbms_output.put_line(v_fName|| ', ' || v_title ||', ' || v_startDate ); --输出结果
exit when my_cur%notfound; --跳出循环的条件
end loop;
close my_cur; --关闭
end;
方法二:改写方法一
下面是这个例子的第二中写法:不需要很麻烦的定义三个变量来存放数据,可以直接定义一个游标型的变量来存放游标里面存放的所有的数据
declare
--v_fName varchar2(128);
--v_title s_emp.title%type;
--v_startDate s_emp.start_date%type;
CURSOR my_cur IS
select first_name|| ' '||last_name n,title,start_date sd from s_emp where salary<1500;
--为了方便起见,我们给上面查询到的字段起了一个别名
emp my_cur%rowtype;--直接定义一个游标类型一样的变量,用来存放所有的数据
begin
open my_cur;
loop
fetch my_cur into emp;--直接把数据给扔到上面定义的变量里面
dbms_output.put_line(emp.n|| ', ' || emp.title ||', ' || emp.sd );
exit when my_cur%notfound;
end loop;
close my_cur;
end;
方法三:直接使用for循环
其实上面括号里面的红色部分就是一个游标,由此可以看出,当使用for循环的时候,是最简便的方法
在for循环中,变量因子无需声明,游标也无需显式的打开和关闭,并且也不需要显式的去fetch数据。
当然红色的部分也可以在DECLARE里面事先定义好
9、for循环检索游标的特性
l 系统隐含的定义了一个数据类型为%ROWTYPE的变量,并以此做为循环的计算器
l 系统自动打开游标,不用显式的使用OPEN语句打开
l 系统重复的自动从游标工作区中提取数据并放入计数器变量中
l 系统自动进行%FOUND属性检查以确定是否有数据
l 当游标工作区中所有的记录都被提取完毕或循环中断时,系统自动的关闭游标。
10、带参数的游标
DECLARE
cursor c_student(p_major students.major%type) IS
select * from student where najor = p_major;
BEGIN
OPEN c_student(101); --传入参数
……
注意:
l 定义参数化游标时,只能指定参数的类型,而不能指定参数的长度、精度、刻度,比如VARCHAR2(20),只能是VARCHAR2,后面的不能再有括号里面的数据
l 打开带参数的游标实参的个数和数据类型等必须与游标定义时形参个数和数据类型等相匹配。
11、利用游标更新或删除数据
l 游标定义语法
cursor 游标名 IS
SELECT select语句 FROM 表 FOR UPDATE
【OF 列名】【NOWAIT】
l 注意
Ø 打开游标时对相应的表加锁(通常select操作不会在数据上设置任何锁),其他用户不能对该表进行DML操作
Ø 若数据对象已经被其他会话加锁,则当前会话挂起等待(默认状态),若指定了NOWAIT语句,则不会等待,返回一个Oracle错误
Ø 对于多表查询时,可以通过of子句指定某个要加锁的列表的形式,对特定的表进行加锁,而其他的表不加锁;否则的话所有的表都会加锁,别人就会无法区操作
Ø 当用户执行COMMIT或ROLLBACK操作时,数据上的锁才会被自动的释放
l 其操作的语法
更新或是修改的语法为
UPDATE | DELETE……
where current of 游标名
注意:
如果游标定义时没有使用for UPDATE子句,则不能利用该游标修改或删除数据库中的数据
12、案例
修改员工的工资,如果员工的部门号为10,则工资提高100;如果部门号为20,则工资提高150;如果部门号为30,则工资提高200;否则工资提高250.
declare
cursor c_emp is select * from emp for update;
v_increment number;
begin
for v_emp in c_emp loop
case v_emp.empno
when 10 then v_increment := 100;
when 20 then v_increment := 150;
when 30 then v_increment := 200;
else v_increment := 250;
end case;
update emp set sal=sal + v_increment where current of c_emp;
end loop;
commit; --注意要提交事务
end;
13、隐式游标
l 概念:
Ø 所有的SQL语句都有一个执行的缓冲区,隐式游标就是指向该缓冲区的指针,由系统隐含的打开、处理和关闭。隐式游标有称作SQL游标
Ø 隐式游标主要用于处理insert、UPDATE、DELETE以及单行的select……INTO语句,没有OPEN、fetch、close等操作的命令
l 隐式游标的属性
Ø SQL%isopen:布尔型值,判断隐式游标是否已经打开。对用户而言,该属性值始终为false,因为操作时系统自动打开,操作完后立即自动关闭
Ø SQL%FOUND:布尔型值,判断当前的操作是否会对数据库产生影响。如果有数据的插入、删除、修改或查询到数据,则返回true,否则返回false
Ø SQL%NOTFOUND:布尔型值,和上面是相反的理解
Ø SQL%rowcount:数值型,返回当前操作所涉及的数据库中的行数。
l 下面是一个案例
修改员工号为1000的员工的工资,将他的工资增加100.如果该员工不存在,则向emp表中插入一个员工号为1000,工资为1600的员工
方法一:
begin
update emp set sal=sal+100 where empno=1000;
if sql%NOTFOUND then
INSERT INTO emp(empno,sal) values(1000,1600);
end if;
commit;
end;
方法二:
begin
update emp set sal=sal+100 where empno=1000;
if sql%rowcount = 0 then
INSERT INTO emp(empno,sal) values(1000,1600);
end if;
commit;
end;
14、游标变量
l 概念
游标变量是一个指向多行查询结果集的指针,不与特定的查询绑定,因此具有非常大的灵活性,可以在打开游标变量时定义查询,可以返回不同结构的结果集
l 使用游标变量包括
Ø 游标引用类型(ref cursor)
Ø 声明游标变量
Ø 打开游标变量
Ø 检索游标变量
Ø 关闭游标变量
001、REF CURSOR类型【游标变量类型】
l REF CURSOR分成如下2种:
Ø strong cursor 强类型游标,申明此类型游标时必须指定返回类型,如:
TYPE 游标变量名 IS REF CURSOR RETURN type
type 可以是任务合法的Oracle数据类型
Ø WEAK cursor 若类型游标,申明此类型游标时无需指定返回类型,如:
TYPE 游标变量名 IS REF CURSOR
注:REF CURSOR声明时也是放在DECLARE快中
002、一个强类型的例子
declare
type strong_cursor is ref cursor return s_emp%rowtype;--这是一个强类型
my_c1 strong_cursor;
emp s_emp%rowtype; --由于这是一个强类型,所以只能是跟游标类型相对应的变量
begin
open my_c1 for select * from s_emp where dept_id=41;
dbms_output.put_line('----------输出41部门的员工----------------');
loop
fetch my_c1 into emp;
exit when my_c1%notfound;
dbms_output.put_line(emp.id||''||emp.salary);
end loop;
close my_c1;
open my_c1 for select * from s_emp where salary>1000;
dbms_output.put_line('----------输出工资大于1000的员工 -------------');
loop
fetch my_c1 into emp;
exit when my_c1%notfound;
dbms_output.put_line(emp.id||''||emp.salary);
end loop;
end;
由上面的例子可以看出,使用游标变量使得查询等变的很随意
003、一个若类型的例子
004、sys_refcursor类型
l 系统自带一个类型:sys_refcursor
l 此类型可以用来定义所有:若类型
就像下面一个例子
declare
type id_list is table of integer INDEX BY binary_integer;
type name_list is table of varchar2(200) INDEX BY binary_integer;
ids id_list;
names name_list;
my_cur sys_refcursor;
begin
open my_cur for select id, first_name from s_emp;
fetch my_cur bulk collect INTO ids, names;
close my_cur;
for i in ids.first..ids.last loop
dbms_output.put_line('ID = '||ids(i)||'NAME = '||names(i));
end loop;
end;