一、游标的定义
PL/SQL 是用游标来管理 SQL 的 SELECT 语句的 . 游标是为了处理这些语句而分配的一大块内存 . 它提供了对一个结果集进行逐行处理的能力 , 可看作是一种特殊的指针 . 它与某个查询结果集相关联 , 可以指向结果集的任意位置 , 以便对指定位置的数据进行处理 . 使用它可以在查询数据的同时对数据进行处理 .
二、 游标的分类 : 静态游标和动态游标
在说明游标分类之前, 先说明一下PL/SQL 的两个操作, 游标属性, 变量属性等
SET SERVEROUTPUT ON ;-- 此语句用于开启打印输出功能
DMBS_OUTPUT.PUT_LINE();-- 打印输出并换行.
%FOUND: 判断当前游标是否指向有效的一条行记录. 是则返回TRUE, 否则返回FALSE
%NOTFOUND: 与%FOUND 恰好相反.
%ISOPEN: 判断游标是否打开, 打开则返回TRUE, 否则返回FALSE.
%ROWCOUNT: 判断当前游标在所指向的结果集中提取的行数. 并非所有总记录数
%TYPE: 声明变量的类型与表中的某列的数据类型完全一致.%TYPE 声明有两个好处: 一是你不必知道某列的确切数据类型二是当某列的数据类型改变时不用修改变量的数据类型.
%ROWTYPE: 声明变量的数据类型与表中的行记录数据类型一致. 对于自定义的记录, 则必须声明自己的域.
三、静态游标
静态游标又分为两种: 隐式游标和显式游标
3.1 隐式游标
隐式游标的特点:
- 隐式游标是由PL/SQL 来管理的, 即不需要声明游标语句, 也不需要OPEN,FETCH,CLOSE 操作
- 隐式游标中必须要有select cur_name into [ 变量名或其他数据类型]. 此句完成OPEN,FETCH,CLOSE 操作.
- 隐式游标只能返回一行记录, 如果无符合条件的记录将会出现NO_DATA_FOUND 异常. 如果出现多条记录将出现
TOO_MANY_ROWS 异常 .
-隐式游标只能用SQL% 判断其游标属性
-对于任何位置使用SQL%ISOPEN结果都是FALSE,隐式游标由PL/SQL 管理
-对于在隐式游标位置前使用SQL%FOUND或SQL%NOTFOUND, SQL%ROWCOUNTNULL( 不确定值)
示例表:
表名:create table tmp_200906(id int primary key)
example 1:
结果:
---------------
1
cursor is closed
4
example 2:使用%ROWTYPE
结果:
example 3:自定义RECORD类型
结果:
3.2 显式游标
显示游标特性:
- 显示游标由用户定义, 并由用户来管理游标, 可返回多行记录.
- 通常使用显示游标需要遵守以下规则
声明游标 -> 打开游标 -> 读取数据 -> 关闭游标
但由于语句包含FOR IN LOOP ,OPEN,FETCH,CLOSE 操作所以不在此例 .
-,查看游标属性时使用cur_name%.
example 1:
结果:
2
3
4
6
8
9
11
12
13
14
16
18
19
20
21
22
31
32
33
3.2.1 声明游标参数
cursor name(parameter_1 data_type,parameter_2 data_type...)
IS select statement...
例如:
declare
record_list is record(employee.emp_id%type,employee.emp_name%type)
cursor emp_cur(emp_nbr number,emp_name varchar2(30))
is
select pay_rate from employee where emp_id = emp_nbr;
BEGIN
OPEN emp_curs(1,2);
FETCH emp_cur into record_list;
close emp_cursor;
END;
四、动态游标
首先, 游标变量和游标是两个不同的概念 . 与游标相似 , 游标变量是指向多行查询游标的结果集的当前行.
游标是静态的, 游标变量是动态的, 同时游标变量并不参与与特定的查询绑定,
所以可以为任何兼容的查询打开游标变量, 从而提高灵活性。而且, 还可以将新的值赋予游标变量,
将它作为参数传递给本地和存储过程。 游标变量针对每个 PL/SQL 用户都是可用的,
可以在客户端完全使用游标变量.ORACLE 服务器同样包含 PL/SQL 引擎 ,
可以将游标变量在应用和服务器之间进行传递 .
4.1. 游标变量: 声明游标实际上是创建一个指针, 指针具有数据类型 REF X. REF 是 REFERENCE ,X 是表示类对象.
因此, 游标变量具有数据类型 REF CURSOR.
注 : 游标总是指向相同的查询工作区 , 游标变量能够指向不同的工作区 , 因此游标和游标变量不能互操作.
注:要创建游标变量,必须首先创建一个参考游标类型,然后在这个游标类型的基础上声明一个游标变量.
4.2. 定义 REF CURSOR 类型 , 创建游标变量有两个步骤 :
1) 定义 REF CURSOR 类型
语法格式 :
TYPE ref_type_name
IS
REF CURSOR [RETURN return_type]
说明 :
ref_type_name 是游标类型的名称 ;return_type 必须是一个记录 (record) 或者数据库表中的一行 .(rowtype) .RETURN 子句是可选的.
下面定义 一个 REF CURSOR 类型游标
DELARE
TYPE xs_cur
IS
REF CURSOR RETURN xs%ROWTYPE;
注意 :
REF CURSOR 类型既可以是强类型 , 也可以是弱类型 , 区别是强类型有返回类型 , 弱类型没有 . 如下所示
DECLARE
TYPE xs_cur IS REF CURSOR RETURN xs%ROWTYPE;-- 强类型
TYPE mycur IS REF CURSOR;-- 弱类型
2) 声明这种类型的游标变量 : 一旦定义了 REF CURSOR 类型就可以在 PL/SQL 块或子程序中声明这个游标变量 . 如 :
DECARE
TYPE xs_cur REF CURSOR RETURN xs%ROWTYPE;
xscur xs_cur;
当然 , 在 RETURN 子句中也可以定义自定义的 RECORD 类型 , 如 :
DECLARE
TYPE kc_cj IS RECORD
(
kch number (4),
kcm number(10),
cj number(4,2)
);
TYPE kc_cjcur IS REF CURSOR RETURN kc_cj;
此外 , 也可以声明游标变量作为函数和过程的参数 . 例如 :
DECLARE
TYPE xs_cur IS REF CURSOR RETURN xs%ROWTYPE;
PROCEDURE open_xs (xscur IN OUT xs_cur)IS
......................
3. 控制游标变量
使用游标变量时 , 要遵循以下步骤 :OPEN-FETCH-CLOSE
OPEN 语句与多行查询的游标变量相关联 , 它执行查询 , 标志结果集
语法格式 :
OPEN {cursor_variable|:host_cursor_variable }FOR
{
select_statement|dynamic_string[USING bind_argument[,......]]
}
如 :
IF NOT xscur%ISOPEN THEN
OPEN xscur FOR SELECT * FROM xs;
END IF ;
游标变量同样可以使用游标属性 :%FOUND,%ISOPEN,%ROWTYPE
在使用过程中 , 其他的 OPEN 语句可以为不同的查询打开相同的游标变量 . 因此 , 在重新打开之前 , 不要关闭游标变量 . 可以打开游标 , 并作为参数传递给存储过程 . 如 :
CREATE PACKAGE xs_data AS
...
TYPE xs_cur IS REF CURSOR RETURN xs%ROWTYPE;
RROCEDURE open_xs(xscur IN OUT xs_cur);
END xs_data;
CREATE PACKAGE BODY xs_data AS
...
PROCEDURE open_xs(xscur IN OUT xs_cur)
AS
BEGIN
OPEN xscur FOR SELECT * FROM xs;
END open_xs;
END xs_data;
当声明一个游标变量作为打开游标变量子程序的参数时 , 必须定义 IN OUT 模式 . 也就是说 , 子程序可以将一个打开的游标变量传递给调用者 .下面开始解决一个有趣的问题:
有一个表,只有ID一列,类型为整型数字,里面放了N条记录.如:1,2,3,4,6,7,8,9,11,13,14,15,17,18,21,22,23,30,31,35,36
要求找出不连续的数字.如果如下:
4 6
9 11
15 17
18 21
23 30
31 35
开始实现:
1CREATE TABLE tmp_cursor_test(FID integer,AID integer);
2SELECT ID FROM TMP_200906 order by id asc;结果:
2
3
4
6
8
9
11
13
14
15
16
19
20
21
22
23
25
27
29
30
32
33
运行脚本:
查询结果:
4 6
6 8
9 11
11 13
16 19
23 25
25 27
27 29
30 32