为支持Oracle处理SQL语句,需要创建一个名为上下文区域的内存区域,其中包含用于处理该语句所需的信息,游标是相对于上下文区域的句柄或者指针。
游标有一些属性例如ROWCOUNT。SQL%ROWCOUNT会返回被更新的数据行数
BEGIN
UPDATE ooag_t
SET ooag011 = '王五'
WHERE ooag001 = '00261'
AND ooagent = 70;
-- 输出更新的行数
DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);
END;
在PL/SQL语句块的DECLARE部分对该游标命名,即生成显示游标。
处理显示游标的过程包括如下步骤:
游标声明会定义游标的名称,并把游标名称与某SELECT语句建立关联。
CURSOR c_cursor_name IS select statement
建议定义游标名称时始终使用前缀c_
DECLARE
CURSOR c_OoagCursor IS
SELECT * FROM ooag_t WHERE ooagent = 70;
......
一个记录就是一个复合的数据结构,这意味着它由一个或者多个元素组成。记录非常像数据库表中的一行数据。
PL/SQL支持三种类型的记录:
基于表的记录就是该记录的结构来自于数据库表中的某些字段的列表。基于游标的记录是记录的数据结构匹配预定义游标的元素。为创建一个基于表或游标的记录,可以使用%ROWTYPE属性
record_name table_name pr cursor_name%ROWTYPE
DECLARE
-- vr_ooag 定义了一个和表ooag_t相同的结构
-- 使用vr_ooag.xxx,xxx表示ooag_t中的列
vr_ooag ooag_t%ROWTYPE;
BEGIN
SELECT *
INTO vr_ooag
FROM ooag_t
WHERE ooagent = 70
AND rownum = 1;
DBMS_OUTPUT.PUT_LINE(vr_ooag.ooag011);
END;
上例演示的基于表的记录
下面演示基于游标的记录
DECLARE
-- 定义一个游标
CURSOR c_ooag_ooag011 IS
SELECT ooag001,ooag011
FROM ooag_t
WHERE ooagent = 70;
-- 定义一个记录类型参照游标
vr_ooag_ooag011 c_ooag_ooag011%ROWTYPE;
......
打开游标时,下面的四个动作会自动发生:
打开游标的语法如下:
OPEN cursor_name;
在声明和打开游标之后,就可以从该游标检索数据。
两种游标检索方式:
FETCH cursor_name INTO PL/SQL variables;
或者
FETCH cursor_name INTO PL/SQL record;
当游标中所有数据行被处理后,应该关闭游标。游标关闭的目的是告诉PL/SQL引擎,程序已经不再使用该游标,与该游标相关的资源就可以被释放了。
关闭游标的语法如下:
CLOSE cursor_name;
DECLARE
-- 定义游标
CURSOR c_ooag_cur01 IS
SELECT ooag001,ooag011
FROM ooag_t
WHERE ooagent = 70;
-- 定义一个游标记录
vr_ooag c_ooag_cur01%ROWTYPE;
BEGIN
-- 打开游标
OPEN c_ooag_cur01;
LOOP
--检索游标
FETCH c_ooag_cur01 INTO vr_ooag;
-- 退出循环的条件,当FETCH没有数据时退出循环
EXIT WHEN c_ooag_cur01%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(vr_ooag.ooag001||':'||vr_ooag.ooag011);
END LOOP;
-- 关闭游标
CLOSE c_ooag_cur01;
END;
DECLARE
CURSOR c_ooag_cur01 IS
SELECT ooag001,ooag011
FROM ooag_t
WHERE ooagent = 70;
vr_ooag c_ooag_cur01%ROWTYPE;
BEGIN
OPEN c_ooag_cur01;
LOOP
FETCH c_ooag_cur01 INTO vr_ooag;
-- 退出循环的条件,当FETCH没有数据时退出循环
EXIT WHEN c_ooag_cur01%NOTFOUND;
-- 最后输出检索出来的数据是第几条
DBMS_OUTPUT.PUT_LINE(vr_ooag.ooag001||':'||vr_ooag.ooag011||'----'||c_ooag_cur01%ROWCOUNT);
END LOOP;
CLOSE c_ooag_cur01;
END;
DECLARE
CURSOR c_ooag_cur01 IS
SELECT ooag001,ooag011
FROM ooag_t
WHERE ooagent = 70;
vr_ooag c_ooag_cur01%ROWTYPE;
BEGIN
OPEN c_ooag_cur01;
LOOP
FETCH c_ooag_cur01 INTO vr_ooag;
IF c_ooag_cur01%FOUND THEN
DBMS_OUTPUT.PUT_LINE(vr_ooag.ooag001||':'||vr_ooag.ooag011||'----'||c_ooag_cur01%ROWCOUNT);
ELSE
EXIT;
END IF;
END LOOP;
CLOSE c_ooag_cur01;
EXCEPTION
-- 如果上述语句抛异常了,可能未关闭游标。
WHEN OTHERS THEN
IF c_ooag_cur01%ISOPEN THEN
CLOSE c_ooag_cur01;
END IF;
END;
游标FOR循环是一种简化的语法,借助FOR循环,游标打开、检索、和关闭的过程被隐含地实现。
DECLARE
CURSOR c_ooag_cur01 IS
SELECT ooag001,ooag011
FROM ooag_t
WHERE ooagent = 70;
vr_ooag c_ooag_cur01%ROWTYPE;
BEGIN
FOR vr_ooag IN c_ooag_cur01 LOOP
DBMS_OUTPUT.PUT_LINE(vr_ooag.ooag001||':'||vr_ooag.ooag011||'----'||c_ooag_cur01%ROWCOUNT||'A');
END LOOP;
END;
声明游标时可以使用参数。这样保证游标得到更明确的结果集。
-- 下面这个IN关键字表示的输入参数的意思
CURSOR c_ooag_cur01 (p_ooag001 IN ooag_t.ooag001%TYPE) IS
SELECT ooag001,ooag011
FROM ooag_t
WHERE ooag001 = p_ooag001;
OPEN c_ooag_cur01(parameter_value)
或
FOR r_ooag IN c_ooag_cur01(parameter_value) LOOP
DECLARE
-- 定义一个带参数的游标,这个游标的参数是IN输入参数
CURSOR c_ooag_cur01 (p_ooag001 IN ooag_t.ooag001%TYPE) IS
SELECT ooag001,ooag011
FROM ooag_t
WHERE ooagent = 70
AND ooag001 = p_ooag001;
BEGIN
-- 使用游标时为游标赋值 vr_ooag可以不用定义直接使用
FOR vr_ooag IN c_ooag_cur01('00261')
LOOP
DBMS_OUTPUT.PUT_LINE(vr_ooag.ooag001||':'||vr_ooag.ooag011||'----'||c_ooag_cur01%ROWCOUNT||'A');
END LOOP;
END;
当执行SELECT语句时,不会锁定任何数据行。使用FOR UPDATE的目的是锁定希望更新的数据库表中的数据行,以便于在执行完更新操作和释放该锁之前,其他人不能执行更新操作。COMMIT或者ROLLBACK语句会释放锁。
在每个更新操作之后就使用COMMIT的代价是很高的。通常COMMIT语句位于循环之后。也可使用SAVEPOINT。
DECLARE
CURSOR c_ooag_cur01 IS
SELECT ooag001,ooag011
FROM ooag_t
WHERE ooagent = 70 FOR UPDATE;
BEGIN
FOR r_ooag IN c_ooag_cur01 LOOP
UPDATE ooag_t
SET ooagud001 = 'hello'
WHERE ooag001 = r_ooag.ooag001
AND ooagent = 70;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- 出现异常必须提交或回滚,才会释放FOR UPDATE的锁不然就出问题了。
IF c_ooag_cur01%ISOPEN THEN
CLOSE c_ooag_cur01;
END IF;
COMMIT;
END;
DECLARE
CURSOR c_ooag_cur01 IS
SELECT ooag001,ooag011
FROM ooag_t
-- 只会锁住oogaud001这列
WHERE ooagent = 70 FOR UPDATE OF ooagud001;
BEGIN
FOR r_ooag IN c_ooag_cur01 LOOP
UPDATE ooag_t
SET ooagud001 = 'hello'
WHERE ooag001 = r_ooag.ooag001
AND ooagent = 70;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- 出现异常必须提交或回滚,才会释放FOR UPDATE的锁不然就出问题了。
IF c_ooag_cur01%ISOPEN THEN
CLOSE c_ooag_cur01;
END IF;
COMMIT;
END;
WHERE CURRENT OF 子句的好处是,免于在UPDATE语句中使用WHERE子句。
DECLARE
CURSOR c_ooag_cur01 IS
SELECT ooag001,ooag011
FROM ooag_t
WHERE ooagent = 70 FOR UPDATE;
BEGIN
FOR r_ooag IN c_ooag_cur01 LOOP
UPDATE ooag_t
SET ooagud001 = 'hello1'
-- 表示更新操作只针对当前的游标指针进行操作
WHERE CURRENT OF c_ooag_cur01;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- 出现异常必须提交或回滚,才会释放FOR UPDATE的锁不然就出问题了。
IF c_ooag_cur01%ISOPEN THEN
CLOSE c_ooag_cur01;
END IF;
COMMIT;
END;