一、游标(含义)
oracle在我们进行查询操作时所得到的结果总是一次性返回给我们一行或多行,但实际上,oracle在检索行的时候,总是一行一行的放入游标中,再逐行进行处理(放一行处理一行),游标的含义相当于一个指针。
如上图所示,游标可以一行一行的控制查询出来的记录,也就是实现对查询记录的行操作的控制。然后我们可以用编程的方式访问数据。
举个例子:存在两张邮箱表,表A和表B,现在表A中邮箱记录全都合格,表B中存在不合格的邮箱记录,我们要将表B中合格的邮箱记录且在表A中不存在的记录插入到表B中,我们就要用到游标,因为此时,我们有简单的DML语句是无法达成的。
二、游标的分类及详细介绍:
**三大类: ** 显式游标 隐式游标 REF游标
(1)显式游标: 由用户显式声明的,自己命名的,显式游标主要是用于对查询语句的处理,尤其是在查询结果为多条记录的情况下,用于处理返回多行的查询 。select
- 声明 打开 读取 关闭 四大步骤 在sql server中多一个deallocte游标(丢弃)步骤。
- 声明 cursor cur_name [参数名 类型:=初始值] is select statement;
- 打开 open cur_name(参数值);
- 读取 fetch cur_name into variable(…); #variable已经被声明的变量列表。
- 关闭 close cur_name
例子一:
SQL>set serveroutput on; -- 打开显示开关
SQL>DECLARE
my_toy_price toys.toyprice%TYPE;
CURSOR toy_cur IS -- 声明显式游标
SELECT toyprice FROM toys -- 显式游标的select语句
WHERE toyprice<250;
BEGIN
OPEN toy_cur; -- 打开游标
LOOP
FETCH toy_cur INTO my_toy_price; -- 提取行
EXIT WHEN toy_cur%NOTFOUND; -- loop结束条件
DBMS_OUTPUT.PUT_LINE ('TOYPRICE=:玩具单价=:'||my_toy_price);
END LOOP;
CLOSE toy_cur; --关闭游标
END;
例子二,存在表yy,两列一列为sno 学号,一列为zz 住址,通过使用游标提取数据:
set serveroutput on;
SQL> declare
2 yy1 yy%rowtype;
3 cursor mycursor is select * from yy;
4 begin
5 open mycursor;
6 fetch mycursor into yy1;
7 while mycursor%found loop
8 dbms_output.put_line('学号是:'||yy1.sno||'住址'||yy1.zz);
9 fetch mycursor into yy1;
10 end loop;
11 close mycursor;
12 end;
13 /
例子二plus 带参数的游标: 实现输入学号几显示学号几的学生。
SQL> declare
sno1 yy.sno%type; -- 声明变量sno1 用来存放用户输入的数
yy1 yy%rowtype; -- 声明yy1 类型为yy表的行类型
cursor mycursor(input_no number) is select * from yy where sno=input_no; --给cursor加参
begin
sno1 :=&输入学号; -- &符号表示需要用户从键盘上输入
open mycursor(sno1); -- 打开游标时传入参数
fetch mycursor into yy1;
while mycursor%found loop
dbms_output.put_line('学号是:'||yy1.sno||'住址'||yy1.zz);
fetch mycursor into yy1;
end loop;
close mycursor;
end;
/
例子三: 使用显式游标来更新行,select… for update; where current of mycursor (指明游标所指定的更新行) ;如果不写 where current of mycursor 所执行的更新操作对所有行都有作用并且更新了两次,更新两次的原因是 sno=1 or sno=2 有两次均匹配成功。
declare
yy1 yy%rowtype; -- 声明yy1 类型为yy表的行类型
cursor mycursor is select * from yy where sno=1 or sno=2 for update; --改动
begin
open mycursor;
fetch mycursor into yy1;
while mycursor%found loop
update yy set sno=sno+5 where current of mycursor; --注意改动不得违反原始精度
fetch mycursor into yy1;
end loop;
close mycursor;
end;
/
(2)隐式游标:由oracle自动创建的,在pl/sql中使用DML语句自动创建,而对于非查询语句,如修改update、删除delete操作,则由ORACLE 系统自动地为这些操作设置游标并创建其工作区,隐式游标的名字为SQL,这是由ORACLE 系统定义的。
- 命名为SQL,自动声明、打开、关闭
- 隐式游标也有相关属性,通过查看相关属性,可以获得最近执行DML语句的信息。
- 多用在update、delete操作上。
set serveroutput on; --打开oracle自带的输出方法dbms_output
begin
update student set sage=sage + 10 ;
dbms_output.put_line('更新了'||sql%rowcount||'行')
end;
/
在上述代码中sql为隐式游标的名字,%rowcount为上述所说的属性,及统计相关sql语句所影响的行数。
- 扩展其他属性:
%FOUND 语句没有影响任何行为时,返回TURE,反之FALSE
%NOTFOUND 语句没有影响任何行为时,返回TURE,反之FALSE
%ROWCOUNT 语句影响行数
%ISOPEN 游标是否打开,隐式游标始终为false
begin
update student set sage=sage +10 where sno>50; -实际上不存在大于50的学生纪录
if sql%found then
dbms_output.put_line('已更新');
else
dbms_output.put_line('没有记录可以更新');
end;
/
(3)REF游标:ref游标用于处理运行时才能确定的动态sql查询结果
REF游标也叫做参照游标(动态游标),REF 游标和游标变量用于处理运行时动态执行的 SQL 查询
- 创建游标变量需要两个步骤:
声明 REF 游标类型
声明 REF 游标类型的变量(依据上述声明类型) - 用于声明 REF 游标类型的语法为:
TYPE <ref_cursor_name> IS REF CURSOR
[RETURN <return_type>]; - 打开游标变量的语法如下:
OPEN cursor_name FOR select_statement;
举例: 实现效果,只准用户查询student表中的相关数据;
type refcur is ref cursur;
cursor2 refcur; -- 定义了一个ref游标变量。
open cursor2 for select sno, sname from student; --打开游标时才指明select语句
三、扩展知识 循环游标 、fetch … bulk collect into
(1)oracle 11g 中的循环游标:
- 循环游标用于简化游标处理代码
当用户需要从游标中提取所有记录时使用
循环游标的语法如下:
FOR <record_index> IN <cursor_name>
LOOP
<executable statements>
END LOOP;
与显示游标中例子二能得到相同的执行结果:简化游标代码
declare
yy1 yy%rowtype;
cursor mycursor is select * from yy;
begin
for cur1 in mycursor loop
dbms_output.put_line('学号是:'||cur1.sno||'住址'|| cur1.zz);
end loop;
end;
/
注意:循环游标不能用来更新,只能用来查询取游标的这种操作
(2 )fetch … bulk collect into : 大大提升检索大量数据的效率
declare
cursor my_cursor is select ename from emp where deptno=10;
type ename_table_type is table of varchar2(10);
-- 定义了一种ename_table_type 类型,其中由长度为10的varchar2组成的table类型。
ename_table ename_table_type;
--定义一个变量 ename_table ,类型为上述定义类型。
begin
open my_cursor;
fetch my_cursor bulk collect into ename_table;
for i in 1..ename_table.count loop
dbms_output.put_line(ename_table(i)); --打印变量ename_table这个变量中的所有内容。
end loop;
close my_cursor;
end;
fetch … bulk collect into 取数据的速度远远大于 fetch … into ,但其在使用时要定义类型。(在大批量数据中看的非常明显)
四、课后题目:
(1)静态游标的联系
已建立表,如下:
create table student (xh number, xm varchar2(10));
insert into student values(1,'A');
insert into student values(2,'B');
insert into student values(3,'C');
insert into student values(4,'D');
create table address (xh number, zz varchar2(10));
insert into address values(2,'郑州');
insert into address values(1,'开封');
insert into address values(3,'洛阳');
insert into address values(4,'新乡');
* 完成的任务:给表student添加一列zz,是varchar2(10)类型;再从address中,
将zz字段的数值取出来,对应的插入到student新增的zz列中。
* 即:得到的结果:student表中,是:
XH XM ZZ
-- ---------- ------
1 A 开封
2 B 郑州
3 C 洛阳
4 D 新乡
①使用普通sql实现:
alter table student add zz varchar2(10);
update student set zz=(select zz from address where address.xh=student.xh );
②使用游标实现:
首先 :alter table student add zz varchar2(10);
(2)REF游标题目
create table student1 (xh number, kc varchar2(10));
insert into student1 values(1,'语文');
insert into student1 values(1,'数学');
insert into student1 values(1,'英语');
insert into student1 values(1,'历史');
insert into student1 values(2,'语文');
insert into student1 values(2,'数学');
insert into student1 values(2,'英语');
insert into student1 values(3,'语文');
insert into student1 values(3,'英语');
完成的任务:
* 生成student2表(xh number, kc varchar2(50));对应于每一个学生,求出他的
总的选课记录,把每个学生的选课记录插入到student2表中。
* 即,student2中的结果如下:
XH KC
--- -------------------------------------------
1 语文数学英语历史
2 语文数学英语
3 语文英语
此时必须要使用游标:外层显式游标明确我们要用该游标取出学号, 内层 REF游标进行拼接操作
declare
xh1 student1.xh%type;
kc1 varchar2(50) :='';
kc2 varchar2(50);
cursor cursor1 is select distinct(xh) from student;
type refcur is ref cursor;
cursor2 refcur;
begin
open cursor1;
fetch cursor1 into xh1;
while cursor1%found loop -- 用cursor1显式游标取每个人的学号
kc1 :='';
open cursor2 for select kc from student1 where xh=xh1;
fetch cursor2 into kc2;
while cursor2%found loop
kc1 :=kc1 || kc2; -- cursor2 实现拼接
fetch cursor2 into kc2; --移动游标指向下一行 内部1相关课程循环
end loop;
close cursor2;
insert into student2 values(xh1,kc1);
commit;
fetch cursor1 into xh1; -- 移动游标指向下一行 外部学号1、2、3循环
end loop;
close cursor1;
end;
/