六、PL/SQL 游标

文章目录

  • 一、游标简介
  • 二、游标类型
  • 三、隐式游标
  • 四、显示游标
    • 4.1 声明游标
    • 4.2 记录类型
    • 4.3 打开游标
    • 4.4 检索游标中的数据行
    • 4.5 关闭游标
    • 4.5 游标的常用属性
    • 4.6 游标示例
    • 4.7 使用游标FOR循环
    • 4.8 在游标中使用参数
      • 4.8.1 如何调用带参数的游标
  • 五、在游标中使用FOR UPDATE
    • 5.1 FOR UPDATE OF
    • 5.2 WHERE CURRENT OF


一、游标简介

为支持Oracle处理SQL语句,需要创建一个名为上下文区域的内存区域,其中包含用于处理该语句所需的信息,游标是相对于上下文区域的句柄或者指针。

二、游标类型

  • 每当SQL语句执行时,Oracle会自动地声明一个隐式游标。
  • 对于返回多行数据的任何世纪公园旬,该程序可以定义显示游标,这个声明允许应用程序顺序地处理游标所返回的每行数据。

三、隐式游标

  • 无论何时SQL语句执行,当该SQL语句不存在显示游标时,都会产生一个隐式游标。
  • 游标自动地与每个DML(数据操作)语句(update,delete和insert)建立关联。
  • 在处理隐式游标的过程中,oracle自动地执行open,fetch和close操作。

游标有一些属性例如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部分对该游标命名,即生成显示游标。
处理显示游标的过程包括如下步骤:

  • 声明游标:这会在内存中建立游标的初始化环境。
  • 打开游标:打开被声明的游标,并分配内存。
  • 检索游标:现在,被声明和打开的游标能够检索数据。
  • 关闭游标:被声明、打开和检索使用后的游标必须被关闭,以释放所分配的内存。

4.1 声明游标

游标声明会定义游标的名称,并把游标名称与某SELECT语句建立关联。
CURSOR c_cursor_name IS select statement

建议定义游标名称时始终使用前缀c_

DECLARE
    CURSOR c_OoagCursor IS
        SELECT * FROM ooag_t WHERE ooagent = 70;
    ......

4.2 记录类型

一个记录就是一个复合的数据结构,这意味着它由一个或者多个元素组成。记录非常像数据库表中的一行数据。

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;
......

4.3 打开游标

打开游标时,下面的四个动作会自动发生:

  • 会检查WHERE子句中的变量(包括绑定变量)。
  • 基于变量的值,决定活跃的数据集,PL/SQL引擎会执行针对该游标的查询。
  • PL/SQL引擎标识活跃的数据集-也就是满足WHERE子句条件,来自于所有所涉及表的数据行。
  • 把活跃数据集的指针设置为第一行数据。

打开游标的语法如下:
OPEN cursor_name;

4.4 检索游标中的数据行

在声明和打开游标之后,就可以从该游标检索数据。

两种游标检索方式:
FETCH cursor_name INTO PL/SQL variables;
或者
FETCH cursor_name INTO PL/SQL record;

  • FETCH用于活跃数据集中,每次检索一行数据。在每个FETCH语句之后,活跃数据集指针继续迁移到下一个数据行。因此,每个FETCH都会返回活跃数据集中连续的数据行,直到获取整个数据集。最后一个FETCH语句不会给输出变量赋值,它仍旧保留原来的值。

4.5 关闭游标

当游标中所有数据行被处理后,应该关闭游标。游标关闭的目的是告诉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;

4.5 游标的常用属性

  • %NOTFOUND cursor_name%NOTFOUND 一个Boolean属性,当前面的FETCH操作没有返回数据行时,该属性的值是TRUE,否则是FALSE
  • %FOUND cursor_name%FOUND 一个Boolean属性,当前面的FETCH操作返回一行数据时,该属性的值是TRUE,否则是FALSE
  • %ROWCOUNT cursor_name%ROWCOUNT 从游标中所检索的记录是第几条
  • %ISOPEN cursor_name%ISOPEN 一个Boolean属性,当游标处于打开状态时,则该属性的值是TRUE,否则是FALSE
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;

4.6 游标示例

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;

4.7 使用游标FOR循环

游标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;

4.8 在游标中使用参数

声明游标时可以使用参数。这样保证游标得到更明确的结果集。

-- 下面这个IN关键字表示的输入参数的意思
CURSOR c_ooag_cur01 (p_ooag001 IN ooag_t.ooag001%TYPE) IS
    SELECT ooag001,ooag011
      FROM ooag_t
     WHERE ooag001 = p_ooag001;

4.8.1 如何调用带参数的游标

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;

五、在游标中使用FOR UPDATE

当执行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;

5.1 FOR UPDATE OF

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;

5.2 WHERE CURRENT OF

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;

你可能感兴趣的:(Oracle,PL/SQL教程,PL/SQL,ORACLE,数据库开发)