1. 简介
在使用 select 语句查询数据时,查询返回的数据存放在结果集中,用户如何从查询得到的结果集中逐行逐列地获取存储的数据,从而在应用程序中使用这些值,就需要一种定位并控制结果集的机制,即游标。
游标是映射在结果集中一行数据上的位置实体,用来处理从数据库中检索的多行记录。利用游标,程序可以逐个地处理和便利一次检索返回的整个记录表,即可以对当前行数据进行操作。
2. 类型
1,隐式游标:在 PL/SQL 程序中执行 DML SQL 语句时自动创建隐式游标,游标名字固定叫 sql。
2,显式游标:显式游标用于处理返回多行的查询。
3,REF 游标:REF (参照)游标用于处理运行时才能确定的动态 SQL 查询的结果。
3. 隐式游标
在 PL/SQL 中使用 DML 语句时自动创建隐式游标。隐式游标自动声明、打开和关闭,其名为 sql。我们可以通过检查隐式游标的属性可以获得最近执行的 DML 语句的信息,隐式游标的属性有:
%FOUND – SQL 语句影响了一行或多行时为 TRUE
%NOTFOUND – SQL 语句没有影响任何行时为TRUE
%ROWCOUNT – SQL 语句影响的行数
%ISOPEN - 游标是否打开,始终为FALSE,因为游标打开执行后会立即关闭。
下面我们举例来说明隐式游标 sql 的存在,首先由如下 student 表:
1 2 3 4 5 6 7 8
| SQL> select * from student;
SNO SNAME SAGE
1 Tom 21 2 Kite 22 3 Bob 23 4 Mike 24
|
编写如下 pl/sql 执行块:
1 2 3 4 5 6 7 8 9
| set serverout on; begin update student set sage = sage + 10; if (sql%found) then dbms_output.put_line('表已更新' || sql%rowcount || '行记录!'); end if; end; / 表已更新4行记录!
|
1 2 3 4 5 6 7 8
| begin update student set sage = sage + 10 where sno = 5; if (sql%notfound) then dbms_output.put_line('表已更新' || sql%rowcount || '行记录!'); end if; end; / 表已更新0行记录!
|
4. 显式游标
显式游标在 PL/SQL 块的声明部分定义查询,该查询可以返回多行。显式游标的操作过程为声明游标、打开游标、使用游标取出记录和关闭游标。我们可以以下述的示例来体现说明,实现效果类似于上述使用隐式游标操作 student 一样:
1 2 3 4 5 6 7 8 9 10 11 12 13
| declare stud student%rowtype; cursor stu_cur is select * from student; begin open stu_cur; loop fetch stu_cur into stud; exit when stu_cur%notfound; dbms_output.put_line('学号是:' || stud.sno || ',姓名是:' || stud.sname); end loop; close stu_cur; end; /
|
输出结果如下所示:
1 2 3 4
| 学号是:1,姓名是:Tom 学号是:2,姓名是:Kite 学号是:3,姓名是:Bob 学号是:4,姓名是:Mike
|
5. 带参数显式游标
声明显式游标时可以带参数以提高灵活性。声明带参数的显式游标的语法如下:
1
| CURSOR <cursor_name>(<param_name> <param_type>) IS select_statement;
|
下述示例我们通过控制台输出序号打印输出执行学生信息,具体如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| declare in_sno number; stud student%rowtype; cursor stu_cur(in_sno number) is select * from student where sno = in_sno; begin in_sno := &学生学号; open stu_cur(in_sno); loop fetch stu_cur into stud; exit when stu_cur%notfound; dbms_output.put_line('学号是:' || stud.sno || ',姓名是:' || stud.sname); end loop; close stu_cur; end; /
|
当我们输出学号 1 的时候,会打印输出如下结果:
6. 使用显式游标更新行
允许使用游标删除或更新活动集中的行,但是声明游标时必须使用 SELECT … FOR UPDATE语句。语法如下所示:
1 2 3 4 5 6 7 8
| CURSOR <cursor_name> IS SELECT statement FOR UPDATE; -- 记得带上 for update
UPDATE <table_name> SET <set_clause> WHERE CURRENT OF <cursor_name>
DELETE FROM <table_name> WHERE CURRENT OF <cursor_name>
|
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| SQL> select * from student;
SNO SNAME SAGE
1 Tom 21 2 Kite 22 3 Bob 23 4 Mike 24 declare stud student%rowtype; cursor stu_cur is select * from student where sno = 2 or sno = 3 for update; begin open stu_cur; loop fetch stu_cur into stud; exit when stu_cur%notfound; update student set sage = sage + 100 where current of stu_cur; end loop; close stu_cur; end; /
|
执行结果如下,其中学号为2 和 3 的学生的年龄增加了 100:
1 2 3 4 5 6 7 8
| SQL> select * from student;
SNO SNAME SAGE
1 Tom 21 2 Kite 122 3 Bob 123 4 Mike 24
|
7. 循环游标
循环游标只适用于查询的情况,不适用于更新和删除的情况
上面的示例中我们中用到了 loop 语句来循环访问游标,同时在访问前后分别需要打开和关闭游标,同时还需要取出游标的记录并赋值到指定的变量中。为了简化操作,我们可以直接使用循环游标。当用户需要从游标中提取所有记录时使用,循环游标的语法如下:
1 2 3 4
| FOR <record_index> IN <cursor_name> LOOP <executable statements> END LOOP;
|
示例如下所示:
1 2 3 4 5 6 7 8
| declare cursor stu_cur is select * from student; begin for cur_sub in stu_cur loop dbms_output.put_line('学号是:' || cur_sub.sno || ',姓名是:' || cur_sub.sname); end loop; end; /
|
执行结果如下所示:
1 2 3 4
| 学号是:1,姓名是:Tom 学号是:2,姓名是:Kite 学号是:3,姓名是:Bob 学号是:4,姓名是:Mike
|
8. 批量处理
可以使用 fetch … bulk collect into 语句来进行批量、快速提取数据,如下是使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| declare cursor my_cursor is select ename from emp where deptno = 10; type ename_table_type is table of varchar2(10); ename_table ename_table_type; 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)); end loop; close my_cursor; end; /
|
查看上述语句的第七行,我们这里使用fetch my_cursor bulk collect into ename_table;
,而不使用fetch my_cursor into ename_table;
是可以提高游标取书速度。
9. REF 游标
REF 游标和游标变量用于处理运行时动态执行的 SQL 查询,创建游标变量需要两个步骤:
声明 REF 游标类型
声明 REF 游标类型的变量
用于声明 REF 游标类型的语法为:
1
| TYPE <ref_cursor_name> IS REF CURSOR [RETURN <return_type>];
|
打开游标变量的语法如下:
1
| OPEN cursor_name FOR select_statement;
|
我们使用 REF 游标来实现通过对输入的学号参数打印输出指定学号的学生信息,由于在声明游标的时候我们无法知道要输出的是哪个学号的学生,因此我们需要使用 REF 游标来实现,示例如下:
student 表中的数据如下所示:
1 2 3 4 5 6 7 8
| SQL> select * from student;
SNO SNAME SAGE
1 Tom 21 2 Kite 22 3 Bob 23 4 Mike 24
|
如下是使用参照游标的具体实现 sql:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| declare type refcur is ref cursor; cursor2 refcur; my_sno student.sno%type; no2 student.sno%type; name2 student.sname%type; begin my_sno := '&学号'; open cursor2 for select sno, sname from student where sno = my_sno; loop fetch cursor2 into no2, name2; exit when cursor2%notfound; dbms_output.put_line('学号是:' || no2 || ',姓名是:' || name2); end loop; close cursor2; end; /
|
当我们输入 4 的时候,执行结果如下所示: