8.3.3 隐式游标的属性
隐式游标的属性和显式游标的属性具体表示含义有区别,但属性种类没有变。下面列出隐式游标的属性。
❑%ISOPEN属性:该属性永远返回FALSE,它由Oracle自己控制。
❑%FOUND属性:此属性可以反应DML操作是否影响到了数据,当DML操作对数据有影响时该属性为TRUE,否则为FALSE。也可以反映出SELECT INTO语句是否返回了数据,当有数据返回时该属性为TURE。
❑%NOTFOUND属性:与%FOUND属性相反,当DML操作没有影响数据以及SELECT INTO没有返回数据时该属性为TRUE,其他为FALSE。
❑%ROWCOUNT属性:该属性可以反映出DML操作对数据影响的数量。
下面利用几个示例来了解以上4个属性的具体使用方法。
1)隐式游标中的%ISOPEN属性和显式游标中的该属性不同,隐式游标该属性永远为FALSE。
【示例12】隐式游标中%ISOPEN的使用
下面的脚本验证了%ISOPEN属性返回FALSE的特性。
01 DECLARE
02 BEGIN
03 DELETE FROM PRODUCTINFO;
04 IF SQL%ISOPEN THEN
05 DBMS_OUTPUT.PUT_LINE('游标打开!');
06 ELSE
07 DBMS_OUTPUT.PUT_LINE('游标未打开!');
08 END IF;
09 END;
【执行效果】
以上脚本执行后输出结果如下:
游标未打开!
由执行结果可以看出此属性的值是FALSE。当第3行执行完成后它由Oracle自动关闭。
2)%FOUND属性在INSERT、UPDATE、DELETE执行对数据有影响时会返回TRUE,而SELECT INTO语句只要有数据返回,该属性就为TRUE。
【示例13】隐式游标中%FOUND的使用
具体代码如下:
01 DECLARE
02 cur_productname productinfo.Productname%TYPE;
03 cur_productprice productinfo.Productprice%TYPE;
04 BEGIN
05 SELECT productname,productprice INTO cur_productname,cur_productprice
06 FROM PRODUCTINFO;
07
08 EXCEPTION
09 WHEN TOO_MANY_ROWS THEN
10 IF SQL%FOUND THEN
11 DBMS_OUTPUT.PUT_LINE('%FOUND为TRUE');
12 DELETE FROM PRODUCTINFO WHERE productid='00000000';
13 IF SQL%FOUND THEN
14 DBMS_OUTPUT.PUT_LINE('删除数据!');
15 END IF;
16 END IF;
17
18 END;
【代码解析】
❑第1~4行读者理解应该不会有问题,这里不再介绍。
❑第5~6行利用SELECT INTO语句向变量中保存数据,但此语句会返回多条数据,也就是说会引发异常。
❑第9行是此语句可能引发的异常,当返回多条数据时会出现TOO_MANY_ROWS异常,脚本会进入THEN后面的代码流程。
❑第10行在发生TOO_MANY_ROWS异常时检测%FOUND是否为TRUE,如果为TRUE则执行THEN后面的脚本。
❑第12行表示当%FOUND为TRUE时,执行该删除脚本。
❑第13行继续判断该删除脚本产生的游标的%FOUND属性,如果为TRUE,则会执行第14行脚本。
❑第15~18行读者可以自行找到其对应的开始标识。
【执行效果】
执行效果如下:
%FOUND为TRUE
由此脚本的执行效果可以看出该属性的使用方法以及特性,第12行的删除语句由于数据库中没有符合WHERE后的条件的记录,所以它并没有影响任何数据,此时的%FOUND为FALSE,第14行脚本也就没有得到执行。
注意 在SELECT INTO语句中%FOUND不会因语句是否发生异常而改变,只要有返回值该属性就为TRUE。但有异常发生时,执行流程会马上发生改变。也就是说,在异常代码外检查该属性有可能得不到有效执行。第13行的%FOUND是第12行删除语句游标的属性,这一点读者需要注意。
3)%NOTFOUND属性和%FOUND属性在逻辑上是相反的,这里不再给出具体的示例。读者把%FOUND属性的示例稍作修改即可。
4)与显式游标不同的是隐式游标中%ROWCOUNT属性反映了DML操作影响的数据数量,而SELECT INTO语句如果发生TOO_MANY_ROWS异常,那么此属性依然是1,而不是实际符合要求的记录数。
【示例14】隐式游标中%ROWCOUNT的使用
具体代码如下:
01 DECLARE
02 cur_productname productinfo.Productname%TYPE;
03 cur_productprice productinfo.Productprice%TYPE;
04 cur_count varchar(8);
05 BEGIN
06 SELECT productname,productprice INTO cur_productname,cur_productprice
07 FROM PRODUCTINFO;
08
09 EXCEPTION
10 WHEN NO_DATA_FOUND THEN
11 DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);
12 DBMS_OUTPUT.PUT_LINE('没有数据!');
13
14 WHEN TOO_MANY_ROWS THEN
15 cur_count:=SQL%ROWCOUNT;
16 DBMS_OUTPUT.PUT_LINE('SQL%ROWCOUNT值是:'||cur_count);
17 UPDATE PRODUCTINFO SET PRODUCTINFO.productname='';
18 DBMS_OUTPUT.PUT_LINE('修改脚本影响记录数:'||SQL%ROWCOUNT);
19 ROLLBACK;
20 DBMS_OUTPUT.PUT_LINE('脚本回滚后:'||SQL%ROWCOUNT);
21 END;
【代码解析】
❑此脚本运行时会出现TOO_MANY_ROWS异常,然后进入异常块内部。
❑第4行声明一个类型为varchar类型的变量,用此变量存放%ROWCOUNT的值。
❑第15行表示发生异常时把%ROWCOUNT的值保存到变量cur_count中,通过第16行的输出可以测试保存是否成功。
❑第17行修改表PRODUCTINFO中数据。
❑第18行输出修改语句影响的记录数。
❑第19行事务回滚。
❑第20行输出事务回滚后%ROWCOUNT属性值。
【执行效果】
执行效果如下:
SQL%ROWCOUNT值是:1
修改脚本影响记录数:7
修改脚本影响记录数:0
通过以上执行效果可以看到,使用变量cur_count保存%ROWCOUNT值是成功的,开发过程中用户经常使用此方式保留%ROWCOUNT值,以便控制流程时使用。而当SELECT INTO语句发生TOO_MANY_ROWS异常时,%ROWCOUNT属性依然返回1条记录,而不是实际的记录数目。这是需要注意的。
注意 %ROWCOUNT属性和事务没有关系,即使事务回滚,它的值也不会变成上次操作的值,这可以从第20行的输出结果看出来。