/** 测试语句1 */
create or replace procedure test_cursor1 as
cursor c1 is select * from procedure_test;
id procedure_test.id%type;
v1 procedure_test.value1%type;
v2 procedure_test.value2%type;
v3 procedure_test.value3%type;
v4 procedure_test.value4%type;
begin
open c1;
loop
fetch c1 into id, v1, v2, v3, v4;
dbms_output.put_line( 'id=' || id || ' v1=' || v1 || ' v2=' || v2 || ' v3=' || v3 || ' v4=' || v4 );
exit when c1%notfound;
end loop;
close c1;
end;
/** 数据
1 1000 a 1000 a
2 1001 b 1001 b
3 1002 c 1002 c
4 1003 d 1003 d
5 1004 e 1004 e
6 1005 f 1005 f
7 1006 g 1006 g
*/
/** 输出
id=1 v1=1000 v2=a v3=1000 v4=a
id=2 v1=1001 v2=b v3=1001 v4=b
id=3 v1=1002 v2=c v3=1002 v4=c
id=4 v1=1003 v2=d v3=1003 v4=d
id=5 v1=1004 v2=e v3=1004 v4=e
id=6 v1=1005 v2=f v3=1005 v4=f
id=7 v1=1006 v2=g v3=1006 v4=g
id=7 v1=1006 v2=g v3=1006 v4=g
*/
/** 测试心得
结合例子revert_date来看,可以发现用for循环来访问游标时,游标是
自动打开和关闭的,但使用loop -- exit -- end loop组和的时候必须
手动打开游标和关闭游标,并且游标的移动也是通过fetch来手动控制
的*/
/** 测试语句2 */
create or replace procedure test_cursor2 as
cursor c1 is select * from procedure_test;
id procedure_test.id%type;
v1 procedure_test.value1%type;
v2 procedure_test.value2%type;
v3 procedure_test.value3%type;
v4 procedure_test.value4%type;
begin
/** 在这里访问cursor的found属性是非法的 */
--if c1%found then
-- dbms_output.put_line( 'befor open cursor is open!' );
--else
-- dbms_output.put_line( 'befor open cursor is close!' );
--end if;
open c1;
if c1%found then
dbms_output.put_line( 'after open cursor is open!' );
else
dbms_output.put_line( 'after open cursor is close!' );
end if;
while c1%found loop
fetch c1 into id, v1, v2, v3, v4;
dbms_output.put_line( 'id=' || id || ' v1=' || v1 || ' v2=' || v2 || ' v3=' || v3 || ' v4=' || v4 );
end loop;
close c1;
/** 在这里访问cursor的found属性也是非法的 */
--if c1%found then
-- dbms_output.put_line( 'befor open cursor is open!' );
--else
-- dbms_output.put_line( 'befor open cursor is close!' );
--end if;
end;
/** 输出
after open cursor is close!
*/
/** 测试心得
在这个例子中使用了cursor的found属性,在open之前和close之后对游标found属性
的访问是非法的。在open之后cursor的found属性仍然为false另外值得注意的是在
fetch之前cursor的found属性是false所以上面这个例子根本没有进入循环,输出为
空。所以用while loop -- end loop 循环来访问cursor必须在进入循环前fetch一
次,这样的写法给人的感觉很不好*/
/** 测试语句3 */
create or replace procedure test_cursor3 as
cursor c1 is select * from procedure_test for update;
begin
if c1%isopen then
dbms_output.put_line( 'before enter the for-cycle cursor is open!' );
else
dbms_output.put_line( 'before enter the for-cycle cursor is close!' );
end if;
for cline in c1 loop
if cline.id = '1' then
if c1%isopen then
dbms_output.put_line( 'aftre enter the for-cycle cursor is auto open!' );
end if;
end if;
dbms_output.put_line( 'id=' || cline.id || ' v1=' || cline.value1 || ' v2=' || cline.value2 || ' v3=' || cline.value3 || ' v4=' || cline.value4 );
update procedure_test set value1 = 1, value2 = 'a', value3 = 2, value4 = 'b' where current of c1;
dbms_output.put_line( 'id=' || cline.id || ' v1=' || cline.value1 || ' v2=' || cline.value2 || ' v3=' || cline.value3 || ' v4=' || cline.value4 );
end loop;
if c1%isopen then
dbms_output.put_line( 'aftre get out the for-cycle cursor is still open!' );
else
dbms_output.put_line( 'aftre get out the for-cycle cursor is auto close!' );
end if;
commit;
end;
/** 运行前
1 1000 a 1000 a
2 1001 b 1001 b
3 1002 c 1002 c
4 1003 d 1003 d
5 1004 e 1004 e
6 1005 f 1005 f
7 1006 g 1006 g
*/
/** 运行后
1 1 a 2 b
2 1 a 2 b
3 1 a 2 b
4 1 a 2 b
5 1 a 2 b
6 1 a 2 b
7 1 a 2 b
*/
/** 输出
before enter the for-cycle cursor is close!
aftre enter the for-cycle cursor is auto open!
id=1 v1=1000 v2=a v3=1000 v4=a
id=1 v1=1000 v2=a v3=1000 v4=a
id=2 v1=1001 v2=b v3=1001 v4=b
id=2 v1=1001 v2=b v3=1001 v4=b
id=3 v1=1002 v2=c v3=1002 v4=c
id=3 v1=1002 v2=c v3=1002 v4=c
id=4 v1=1003 v2=d v3=1003 v4=d
id=4 v1=1003 v2=d v3=1003 v4=d
id=5 v1=1004 v2=e v3=1004 v4=e
id=5 v1=1004 v2=e v3=1004 v4=e
id=6 v1=1005 v2=f v3=1005 v4=f
id=6 v1=1005 v2=f v3=1005 v4=f
id=7 v1=1006 v2=g v3=1006 v4=g
id=7 v1=1006 v2=g v3=1006 v4=g
aftre get out the for-cycle cursor is auto close!
*/
/** 测试心得
这里可以更清楚地看到for循环自动的打开和关闭cursor的过程,而且
可以看到利用游标来更新数据集的写法,只要稍加改变,他们的组合就
可以实现非常强大的功能,就比如在revert_data中实现的那样,值得
注意的是在update后,马上引用,当前行的值并没有改变尝试将语句改
写为test_cursor4的形式看看结果
*/
/** 测试语句4 *
create or replace procedure test_cursor3 as
cursor c1 is select * from procedure_test for update;
begin
if c1%isopen then
dbms_output.put_line( 'before enter the for-cycle cursor is open!' );
else
dbms_output.put_line( 'before enter the for-cycle cursor is close!' );
end if;
for cline in c1 loop
if cline.id = '1' then
if c1%isopen then
dbms_output.put_line( 'aftre enter the for-cycle cursor is auto open!' );
end if;
end if;
dbms_output.put_line( 'id=' || cline.id || ' v1=' || cline.value1 || ' v2=' || cline.value2 || ' v3=' || cline.value3 || ' v4=' || cline.value4 );
update procedure_test set value1 = 1, value2 = 'a', value3 = 2, value4 = 'b' where current of c1;
commit;
dbms_output.put_line( 'id=' || cline.id || ' v1=' || cline.value1 || ' v2=' || cline.value2 || ' v3=' || cline.value3 || ' v4=' || cline.value4 );
end loop;
if c1%isopen then
dbms_output.put_line( 'aftre get out the for-cycle cursor is still open!' );
else
dbms_output.put_line( 'aftre get out the for-cycle cursor is auto close!' );
end if;
commit;
end;
/** 测试结果
在行(for cline in c1 loop)出错,提示违反读取顺序
*/
/** 测试心得
说明在for循环内部不能出现commit语句,for循环有更深的要求
需要查找其他资料
*/
/** 其他说明
当执行一条DML语句后,DML语句的结果保存在四个游标属性中,
这些属性用于控制程序流程或者了解程序的状态。当运行DML语
句时,PL/SQL打开一个内建游标并处理结果,游标是维护查询结
果的内存中的一个区域,游标在运行DML语句时打开,完成后关
闭。隐式游标只使用SQL%FOUND,SQL%NOTFOUND,SQL%ROWCOUNT三个
属性.SQL%FOUND,SQL%NOTFOUND是布尔值,SQL%ROWCOUNT是整数值。
SQL%FOUND和SQL%NOTFOUND在执行任何DML语句前SQL%FOUND和
SQL%NOTFOUND的值都是NULL,在执行DML语句后,SQL%FOUND的属性
值将是:
. TRUE :INSERT
. TRUE :DELETE和UPDATE,至少有一行被DELETE或UPDATE.
. TRUE :SELECT INTO至少返回一行
当SQL%FOUND为TRUE时,SQL%NOTFOUND为FALSE。
SQL%ROWCOUNT
在执行任何DML语句之前,SQL%ROWCOUNT的值都是NULL,对于
SELECT INTO语句,如果执行成功,SQL%ROWCOUNT的值为1,如果没有
成功,SQL%ROWCOUNT的值为0,同时产生一个异常NO_DATA_FOUND.
SQL%ISOPEN是一个布尔值,如果游标打开,则为TRUE, 如果游标关
闭,则为FALSE.对于隐式游标而言SQL%ISOPEN总是FALSE,这是因为
隐式游标在DML语句执行时打开,结束时就立即关闭。
*/
/** 备注
隐式游标将在文件test_hidden_cursor中说明
*/