DMSQL 程序中使用 SELECT…INTO 语句将查询结果存放到变量中进行处理的方法,但这种方法只能返回一条记录,否则就会产生 TOO_MANY_ROWS 错误。为了解决这个问题,DMSQL 程序引入了游标,允许程序对多行数据(结果集)进行逐条处理
DM 游标分为静态游标和动态游标两类
游标和游标变量的关系就是常量和变量的关系
引用游标是一种特殊类型的游标变量
静态游标
静态游标是只读游标,它总是按照打开游标时的原样显示结果集,在编译时就能确定静态游标使用的查询
静态游标又分为两种:隐式游标和显式游标
隐式游标
隐式游标无需用户进行定义,每当用户在 DMSQL 程序中执行一个 DML 语句(INSERT、UPDATE、DELETE)或者 SELECT …INTO 语句时,DMSQL 程序都会自动声明一个隐式游标并管理这个游标。
隐式游标的名称统一称为“SQL”,用户可以通过隐式游标获取语句执行的一些信息
使用隐式游标的步骤
数据库自动执行隐式游标的相关操作。比如:打开游标、提取数据,关闭游标等
隐式游标的属性
DMSQL 程序中的每个游标都有%FOUND、%NOTFOUND、%ISOPEN 和%ROWCOUNT四个属性。但是隐式游标和显式游标的属性意义不同。对于隐式游标,这四个属性的意义如下:
%FOUND:语句是否修改或查询到了记录,是返回 TRUE,否则返回 FALSE;
%NOTFOUND:语句是否未能成功修改或查询到记录,是返回 TRUE,否则返回FALSE;
%ISOPEN:游标是否打开。是返回 TRUE,否返回 FALSE。由于系统在语句执行完成后会自动关闭隐式游标,因此隐式游标的%ISOPEN 属性永远为 FALSE;
%ROWCOUNT:DML 语句执行影响的行数,或 SELECT…INTO 语句返回的行数
示例,将孙丽的电话号码修改为 13818882888。示例中的“SQL”为隐式游标的名称:
BEGIN
UPDATE PERSON.PERSON SET PHONE=13818882888 WHERE NAME='孙丽';
IF SQL%NOTFOUND THEN
PRINT '此人不存在';
ELSE
PRINT '已修改';
END IF;
END;
/
显式游标
显式游标指向一个查询语句执行后的结果集区域。当需要处理返回多条记录的查询时,应显式地定义游标以处理结果集的每一行。
使用显式游标的步骤
使用显式游标一般包括四个步骤:
- 定义游标:在 DMSQL 程序的声明部分定义游标,声明游标及其关联的查询语句;
- 打开游标:执行游标关联的语句,将查询结果装入游标工作区,将游标定位到结果集的第一行之前;
- 拨动游标:根据应用需要将游标位置移动到结果集的合适位置,获取数据;
- 关闭游标:游标使用完后应关闭,以释放其占有的资源。
定义显式游标
在 DMSQL 程序的声明部分定义显式游标
CURSOR <游标名> [FAST | NO FAST] <cursor 选项>; |
<游标名> CURSOR [FAST | NO FAST] <cursor 选项>;
<cursor 选项> := <cursor 选项 1>|<cursor 选项 2>|<cursor 选项 3>|<cursor 选项 4>
<cursor 选项 1>:= <IS|FOR> {<查询表达式>|<连接表>}
<cursor 选项 2>:= <IS|FOR> TABLE <表名>
<cursor 选项 3>:= (<参数声明> {,<参数声明>})IS <查询表达式>
<cursor 选项 4>:= [(<参数声明> {,<参数声明>})] RETURN <DMSQL 数据类型> IS <查询表达式>
<参数声明> ::= <参数名> [IN] <参数类型> [ DEFAULT|:= <缺省值> ]
<DMSQL 数据类型> ::= <普通数据类型>
| <变量名> %TYPE
| <表名> %ROWTYPE
| CURSOR
| REF <游标名>
示例:
DECLARE
CURSOR c1 IS SELECT TITLE FROM RESOURCES.EMPLOYEE WHERE MANAGERID = 3;
CURSOR c2 RETURN RESOURCES.EMPLOYEE%ROWTYPE IS SELECT * FROM RESOURCES.EMPLOYEE;
c3 CURSOR IS TABLE RESOURCES.EMPLOYEE;
……
打开显式游标
OPEN <游标名>;
拨动游标
将游标拨动到结果集的某个位置,获取数据
FETCH [<fetch 选项> [FROM]] <游标名> [ [BULK COLLECT] INTO <主变量名>{,<主变量名>} ] [LIMIT
<rows>];
<fetch 选项>::= NEXT|PRIOR|FIRST|LAST|ABSOLUTE n|RELATIVE n
fetch 选项指定将游标移动到结果集的某个位置:
NEXT:游标下移一行
PRIOR:游标前移一行
FIRST:游标移动到第一行
LAST:游标移动到最后一行
ABSOLUTE n:游标移动到第 n 行
RELATIVE n:游标移动到当前指示行后的第 n 行
示例,使用%NOTFOUND 来判断是否处理完数据并退出循环
DECLARE
v_name VARCHAR(50);
v_phone VARCHAR(50);
c1 CURSOR FOR SELECT NAME,PHONE FROM PERSON.PERSON A,RESOURCES.EMPLOYEE B WHERE
A.PERSONID=B.PERSONID;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO v_name,v_phone;
EXIT WHEN c1%NOTFOUND;
DM8_SQL 程序设计
PRINT v_name || v_phone;
END LOOP;
CLOSE c1;
END;
/
使用 FETCH…BULK COLLECT INTO 可以将查询结果批量地、一次性地赋给集合变量。
FETCH…BULK COLLECT INTO 和 LIMIT rows 配合使用,可以限制每次获取数据的行数
示例,FETCH…BULK COLLECT INTO 的使用方法
DECLARE
TYPE V_rd IS RECORD(V_NAME VARCHAR(50),V_PHONE VARCHAR(50));
TYPE V_type IS TABLE OF V_rd INDEX BY INT;
v_info V_type;
c1 CURSOR IS SELECT NAME,PHONE FROM PERSON.PERSON A,RESOURCES.EMPLOYEE B WHERE
A.PERSONID=B.PERSONID;
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO v_info;
CLOSE c1;
FOR I IN 1..v_info.COUNT LOOP
PRINT v_info(I).V_NAME ||v_info(I).V_PHONE;
END LOOP;
END;
/
BULK COLLECT 可以和 SELECT INTO、FETCH INTO、RETURNING INTO 一起使用,
BULK COLLECT 之后 INTO 的变量必须是集合类型。针对 FETCH…BULK COLLECT INTO 语
句,BULK COLLECT 之后 INTO 的变量不支持索引类型为 VARCHAR 的索引表
关闭游标
CLOSE <游标名>;
显式游标的属性
同样地,每一个显式游标也有%FOUND、%NOTFOUND、%ISOPEN 和%ROWCOUNT 四个属性,但这些属性的意义与隐式游标的有一些区别
%FOUND:如果游标未打开,产生一个异常。否则,在第一次拨动游标之前,其值为 NULL。如果最近一次拨动游标时取到了数据,其值为 TRUE,否则为 FALSE。
%NOTFOUND:如果游标未打开,产生一个异常。否则,在第一次拨动游标之前,其值为 NULL。如果最近一次拨动游标时取到了数据,其值为 FALSE,否则为 TRUE。
%ISOPEN:游标打开时为 TRUE,否则为 FALSE。
%ROWCOUNT:如果游标未打开,产生一个异常。如游标已打开,在第一次拨动游标之前其值为 0,否则为最近一次拨动后已经取到的元组数。
示例,显式游标属性的使用方法。对于基表 EMPSALARY,输出表中的前 5 行数据。如果表中的数据不足 5 行,则输出表中的全部数据:
DECLARE
CURSOR c1 FOR SELECT * FROM OTHER.EMPSALARY;
my_ename CHAR(10);
my_empno NUMERIC(4);
my_sal NUMERIC(7,2);
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO my_ename, my_empno, my_sal;
EXIT WHEN c1%NOTFOUND; //当游标取不到数据时跳出循环
PRINT my_ename || ' ' || my_empno || ' ' || my_sal;
EXIT WHEN c1%ROWCOUNT=5; //已经输出了 5 行数据,跳出循环
END LOOP;
CLOSE c1;
END;
/
动态游标
与静态游标不同,动态游标在声明部分只是先声明一个游标类型的变量,并不指定其关联的查询语句,在执行部分打开游标时才指定查询语句。
动态游标的使用主要在定义和打开时与显式游标不同
定义动态游标
CURSOR <游标名>;
打开动态游标
OPEN [WITH FAST]<游标名><for 表达式>;
<for 表达式>::=<for_item1>|<for_item2>
<for_item1>::= FOR <查询表达式>
<for_item2>::= FOR <表达式> [USING <绑定参数> {,<绑定参数>}]
动态游标在 OPEN 时通过 FOR 子句指定与其关联的查询语句。
WITH FAST:在打开游标时指定 FAST 属性。OPEN 语句中的 WITH FAST 必须在
DM.INI 参数 ENABLE_FAST_REFCURSOR=1 的情况下才能生效,否则只是语法支持并未生
效。
示例,使用动态游标输出员工的姓名、工号和薪水:
DECLARE
my_ename CHAR(10);
my_empno NUMERIC(4);
my_sal NUMERIC(7,2);
CURSOR c1;
BEGIN
OPEN C1 FOR SELECT * FROM OTHER.EMPSALARY;
LOOP
FETCH c1 INTO my_ename, my_empno, my_sal;
EXIT WHEN c1%NOTFOUND;
PRINT '姓名'||my_ename || '工号' || my_empno || ' 薪水' || my_sal;
END LOOP;
CLOSE c1;
END;
/
态游标关联的查询语句还可以带有参数,参数以“?”指定,同时在打开游标语句中使
用 USING 子句指定参数,且参数的个数和类型与语句中的“?”必须一一匹配。
示例,使用关联的语句中带有参数的动态游标:
DECLARE
str VARCHAR;
CURSOR csr;
BEGIN
OPEN csr FOR 'SELECT LOGINID FROM RESOURCES.EMPLOYEE WHERE TITLE =? OR TITLE =?'
USING '销售经理','总经理';
LOOP
FETCH csr INTO str;
EXIT WHEN csr%NOTFOUND;
PRINT str;
END LOOP;
CLOSE csr;
END;
/
拨动和关闭游标
与显式游标相同
参考:
https://eco.dameng.com/