ORACLE PL/SQL从入门到精通笔记-使用游(光)标(十一)

此书及其相关资源下载方法:关注微信公众号,点击功能介绍-书籍链接下载,即可获取

说明

ORACLE PL/SQL从入门到精通的事务处理和锁定,异常处理机制将会在<ORACLE官方文档>的笔记中具体详细介绍,至此ORACLE PL/SQL的入门和基础已经进入完结
本文主要介绍如下:

  • 12.1游标基本结构
    12.1.1 游标简介
    12.1.2 游标分类
    12.1.3 定义游标类型
    12.1.4 打开游标
    12.1.5 使用游标属性
    12.1.6 提取游标数据
    12.1.7 批量提取游标数据
    12.1.8 关闭游标
  • 12.2操纵游标数据
    12.2.1 LOOP循环
    12.2.2 WHILE循环
    12.2.3游标FOR循环
    12.2.4 修改游标数据
  • 12.3游标变量
    12.3.1 游标变量简介
    12.3.2 声明游标变量类型
    12.3.3 定义游标变量
    12.3.4 打开游标变量
    12.3.5 控制游标变量
    12.3.6 处理游标变量异常
    12.3.7 在包中使用游标变量
    12.3.8 游标变量的限制

-----------------------------------12.1 游标基本结构-----------------------------------

12.1.1 游标简介

定义

游标实际上指向的是一块内存区域,这块内存区域位于进程全局区内部,称为上下文
区域 (ContextAIea) ,在上下文区域中保存了如下3类信息。

  • 查询返回的数据行。
  • 查询所处理的数据的行号。
  • 指向共享池中的己分析的 SQL语句。

代码部分

DECLARE
   emprow   emp%ROWTYPE;     --定义保存游标检索结果行的记录变量
   CURSOR emp_cur            --定义游标
   IS
      SELECT *
        FROM emp
       WHERE deptno IS NOT NULL;
BEGIN
   OPEN emp_cur;             --打开游标
   LOOP                      --循环检索游标
      FETCH emp_cur          --提取游标内容
       INTO emprow;
      --输出检索到的游标行的信息
      DBMS_OUTPUT.put_line (   '员工编号:'
                            || emprow.empno
                            || ' '
                            || '员工名称:'
                            || emprow.ename
                           );
      EXIT WHEN emp_cur%NOTFOUND;  --当游标数据检索完成退出循环
   END LOOP;   
   CLOSE emp_cur;           --关闭游标
END;

执行过程如图所示

执行过程文字说明

  • 上述语句的执行过程如下所示:
    (l) 在语句的声明块中, 声明了一个用来保存游标检索结果的记录类型,指向 emp表
    的行记录.
    (2) 在声明块中, 使用 CURSOR 关键字定义了一个游标, 在IS 子句后面指定了查询
    语句查询 emp表中的数据.
    (3) 在执行块中, 首先使用 OPEN 语旬打开emp_cur游标, 此时查询数据将保存到内
    存中' 游标就指向该内存区域.
    (4)通过 FETCH语句提取游标中的数据到游标行中.
    (5) 输出游标数据到屏幕上.
    (6) 使用游标变量%NOTFOUND判断记录是否己检索完.
    (7) 循环结束, 必须关闭游标.
  • 通过上面的示例y 可以发现一个显式的游标由如下的几个过程实现:
    (1) 声明游标并为游标关联SELECT语句.
    (2)执行 SELECT语句打开游标。
    (3)将游标的结果放到PL/SQL变量中.
    (4) 关闭游标。

12.1.2 游标分类

  • 显式游标:使用 CURSOR语句显式定义的游标, 游标被定义之后, 需要打开并提
    取游标。
  • 隐式游标: 由0racle 为每一个不属于显式游标的 SQL DML 语句都创建一个隐式
    的游标, 由于隐式游标没有名称, 困此也可以叫做SQL游标。

12.1.3 定义游标类型

指定的SELECT语句。 显式游标的定义语法如下所示。

CURSOR cursor_name [parameter_list]
[RETURN return_type]
IS query
[FOR UPDATE [OF (column_11st)] [NOWAIT] ]:

语法说明

声明语句中关键部分的含义如下。

  • cursor_naIne:用于指定一个有效的游标名称, 这个名称遵循 PL/SQL 的标识符命
    名规范。
  • parameter_list: 用于指定一个或多个可选的游标参数, 这些参数将用于查询执行。
  • RETURN return_type:可选的 RETURN 子句指定游标将要返回的由 return_type 指
    定的数据类型, return_type必须是记录或数据表的行类型。
  • query: 可以是任何 SELECT语句。
  • FOR UPDATE: 指定该子句将在游标打开期间锁定游标记录, 这些记录对其他用
    户来说为只读模式。

12.1.4 打开游标

打开游标的语法如下所示。

OPEN cursor_name [ (parameter_values

语法说明

curs0r_name 用于指定游标名称,parameter_values 用于指定游标的参数列表。 在游标
被打开时, 将会发生如下所示的几个动作。

  • 检验绑定变量的值。
  • 基于查询的语句确定游标的活动集
  • 游标指针指向游标活动集的第l行。

打开游标执行流程

12.1.5 使用游标属性

不论显示还是隐式游标都含有四个属性:%ISOPEN,%FOUND,%NOTFOUND和%ROWCOUNT

%ISOPEN属性

判断对应的游标是否被打开,如果打开:TRUE,否则FALSE

%FOUND属性

在调用FETCH语句获取数据之前,为NULL值,而此后会每取得一行数据返回为TRUE,直到最后一个值被取完后找不到数据就会为FALSE

%NOTFOUND属性

和%FOUND数据完全相反,在调用FETCH语句获取数据之前,为NULL值,而此后会每取得一行数据返回为FALSE,直到最后一个值被取完后找不到数据就会为TRUE

ROWCOUNT属性

游标被打开时,值为0,每获取一条数据,则加1
注意:一定要在被使用的情况下,才会加1,而不是计算查询结果总数据量

12.1.6 提取游标数据

在使用游标时′,只有将游标数据提取出来, 才 能进行进一步的使用
提取游标数据使用 FETCH 语句,该语句可以一次一行地提取游标数据, 也可以使用 BULK COLLECT子句一次性接收所有的游标数据到一个数组或一个PL/SQL表中.

FETCH培句的使用语法如下所示。

FETCH cursor_name INTO variable_name (s) | PL/SQL_record

语法说明

  • cursor_name 用于指定要打开的游标名称,
  • variable_name(s)用来指定一个或多个以逗号分隔的变量,变量的类型匹配在游标定义中使用 SELECT语句查询的字段的顺序。
  • PL/SQL_record 用来指定]个记录类型来一次性接收所有的列数据。

12.1.7 批量提取游标数据

使用BULK COLLECT子句一次性接收所有的游标数据到一个数组或一个PL/SQL表中,一次性获取数据

12.1.8 关闭游标

语法

CLOSE cursor_name;

注意:关闭一个未打开的游标会报异常

游标的限制

使用system登入oracle,dba权限下使用如下语句

show parameter open_cursors;

执行结果

如果需要修改这个限制

alter system set open_cursors=max_open_cursors;--max_open_cursors为number类型,定义最大的游标打开次数

12.1 游标基本结构涉及到的知识点代码如下:

DECLARE
  --声明并定义一个标识符为cur_emp的游标
  --注意:cur_emp不是PL/SQL的变量名,不能赋值或者表达式中使用,但是作用域规则和变量一样
  --如果定义含参数的游标,那么查询的类型也必须一致
  CURSOR cur_emp_all IS
    SELECT s.empno,
           s.ename,
           s.job,
           s.mgr,
           s.hiredate,
           s.sal,
           s.comm,
           s.deptno
      FROM hyz_emp s
     WHERE s.deptno = 20;
  CURSOR cur_emp(v_deptno IN NUMBER) IS
    SELECT s.empno, s.ename FROM hyz_emp s WHERE s.deptno = v_deptno;
  v_empno hyz_emp.empno%TYPE;
  v_ename hyz_emp.ename%TYPE;
  TYPE emp_type IS TABLE OF hyz_emp%ROWTYPE; --定义一个emp表中的行类型的嵌套表类型
  emp_tab emp_type; --定义一个嵌套遍历
BEGIN
  OPEN cur_emp(20); --打开含有参数的参数游标,需要带参数
  --显示游标ISOPEN属性
  IF cur_emp%ISOPEN THEN
    dbms_output.put_line('游标已open');
    --使用LOOP循环,游标打开的情况下才能遍历
    dbms_output.put_line('使用FETCH获取数据后LOOP遍历');
    LOOP
      FETCH cur_emp
        INTO v_empno, v_ename;
      --显示游标NOTFUDN属性
      EXIT WHEN cur_emp%NOTFOUND;
      --显示游标的FOUND属性
      IF cur_emp%FOUND AND cur_emp%ROWCOUNT < 5 THEN
        --显示游标的ROWCOUNT属性,记录能查到的数据条数,而非总条数
        dbms_output.put_line(v_empno || '号员工姓名是' || v_ename || ',已查询到的数据:' ||
                             cur_emp%ROWCOUNT);
      ELSE
        --当已经退出%ROWCOUNT不会加1,而当cur_emp%ROWCOUNT>=5时则仍会加1
        dbms_output.put_line('程序已经退出' || cur_emp%ROWCOUNT);
      END IF;
    END LOOP;
  ELSE
    dbms_output.put_line('游标已close');
  END IF;
  CLOSE cur_emp; --关闭时无需参数
  OPEN cur_emp_all; --打开游标
  --使用FETCH...BULK COLLECT子句一次性遍历游标
  dbms_output.put_line('使用FETCH...BULK COLLECT获取数据后FOR...LOOP遍历');
  FETCH cur_emp_all BULK COLLECT
    INTO emp_tab;
  CLOSE cur_emp_all; --关闭游标
  --即使游标关闭,也可正常使用depttab嵌套变量
  FOR i IN 1 .. emp_tab.count --遍历嵌套变量的数据
   LOOP
    dbms_output.put_line(emp_tab(i).empno || '号员工姓名是' || emp_tab(i).ename);
  END LOOP;
  --比如要把濮军 的姓名改成黄濮军
  UPDATE hyz_emp s SET s.ename = '黄濮军' WHERE s.ename = '濮军';
  IF SQL%FOUND THEN
    --隐式游标的也有%FOUND,%NOTFUND,%ISOPEN和%RWOCOUNT属性
    COMMIT;
    dbms_output.put_line('成功修改员工姓名原为:濮军,现为:黄濮军');
  ELSE
    ROLLBACK;
    dbms_output.put_line('不存在此员工或您已修改过');
  END IF;
  --显示游标ISOEN属性
  IF cur_emp%ISOPEN THEN
    dbms_output.put_line('游标已open');
  ELSE
    dbms_output.put_line('游标已close');
  END IF;
END;

--------------------------------12.2 操纵游标数据--------------------------------

12.2.1 LOOP循环

见12.2代码部分

12.2.2 WHILE循环

见12.2代码部分

12.2.3游标FOR循环

见12.2代码部分

12.2.4 修改游标数据

FOR UPDATE子句

FOR UPDATE 子句会对用 SELECT 语句提取出来的结果进行锁定, 相当下给结果集
的行加了一把互斥锁, 实行行级锁定。 这样其他的用户或会话就不能对当前游标行进行修
改或删除, 直到整个事务被提交为止.

声明
[FOR UPDATE [OF (column_list) ][NOWAIT] ] ;
声明说明

在声明中, 可选的 OF(colunm_list)指定要锁定的表的列, 可以同时指定多个表列进行
锁定: NOWAIT指定当有别的会话锁定相同的行时, SELECT FOR UPDATE语句将不进
行等待而直接返回.

WHERE CURRENT OF子句

使用FOR UPDATE锁定表中的行后,可以在UPDATE或DELETE语句中使用WHERE CURRENT OF子句来得到当前游标所检索出来的行

语法
WHERE CURRENT OF cursorname;
语法说明

cursorname为当前使用FOR UPDATE子句的游标的名称,用来更新游标数据.
注意:使用WHERE CURRENT OF子句检索的游标一定要有FOR UPDATE子句,并且游标要被打开且至少返回一行,不然ORACLE会触发异常

12.2 操纵游标数据涉及到的知识点代码如下:

DECLARE
  CURSOR cur_emp_all IS
    SELECT s.* FROM hyz_emp s WHERE s.deptno = 20;
  v_empno  hyz_emp%ROWTYPE;
  v_empno2 hyz_emp%ROWTYPE;
BEGIN
  dbms_output.put_line('LOOP循环:');
  --LOOP循环
  OPEN cur_emp_all; --打开游标
  LOOP
    FETCH cur_emp_all
      INTO v_empno;
    EXIT WHEN cur_emp_all%NOTFOUND;
    IF cur_emp_all%FOUND THEN
      dbms_output.put_line('员工号:' || v_empno.empno || ',' || '姓名:' ||
                           v_empno.ename || ',' || '职业:' || v_empno.job || ',' ||
                           '上级编号:' || v_empno.mgr || ',' || '入职日期:' ||
                           v_empno.hiredate || ',' || '工资:' || v_empno.sal || ',' ||
                           '奖金:' || v_empno.comm || ',' || '部门号' ||
                           v_empno.deptno);
    ELSE
      dbms_output.put_line('遍历完毕');
    END IF;
  END LOOP;
  CLOSE cur_emp_all; --关闭游标
  dbms_output.put_line('WHILE循环:');
  --WHILE循环
  OPEN cur_emp_all; --打开游标
  FETCH cur_emp_all
    INTO v_empno2;
  WHILE cur_emp_all%FOUND LOOP
    dbms_output.put_line('员工号:' || v_empno2.empno || ',' || '姓名:' ||
                         v_empno2.ename || ',' || '职业:' || v_empno2.job || ',' ||
                         '上级编号:' || v_empno2.mgr || ',' || '入职日期:' ||
                         v_empno2.hiredate || ',' || '工资:' || v_empno2.sal || ',' ||
                         '奖金:' || v_empno2.comm || ',' || '部门号' ||
                         v_empno2.deptno);
    FETCH cur_emp_all
      INTO v_empno2;
  END LOOP;
  CLOSE cur_emp_all; --关闭游标
  dbms_output.put_line('FOR循环:');
  --FOR循环,隐式的自动开关游标,不要在使用前后打开和关闭游标,否则会报错
  FOR cur_emp3 IN cur_emp_all LOOP
    dbms_output.put_line('员工号:' || cur_emp3.empno || ',' || '姓名:' ||
                         cur_emp3.ename || ',' || '职业:' || cur_emp3.job || ',' ||
                         '上级编号:' || cur_emp3.mgr || ',' || '入职日期:' ||
                         cur_emp3.hiredate || ',' || '工资:' || cur_emp3.sal || ',' ||
                         '奖金:' || cur_emp3.comm || ',' || '部门号' ||
                         cur_emp3.deptno);
  END LOOP;
END;
--使用WHERE CURRENT OF
DECLARE
   CURSOR emp_cursor (p_deptno IN NUMBER)
   IS
      SELECT  deptno
            FROM hyz_emp
           WHERE deptno = p_deptno
      FOR UPDATE;                              --使用FOR UPDATE子句添加互斥锁
BEGIN
   FOR emp_row IN emp_cursor (20)              --使用游标FOR循环检索游标
   LOOP
      UPDATE hyz_emp                      
         SET comm = comm * 1.12
       WHERE CURRENT OF emp_cursor;            --使用WHERE CURRENT OF更新游标数据
   END LOOP;
   COMMIT;                                     --提交更改
END;

--------------------------------12.3 游标变量--------------------------------

12.3.1 游标变量简介

可以把静态游标与游标变量想象为常量与变量的区别, 常量的值一经定义就不可改
变, 而变量通过为其赋予不同的值而发生变化。 游标变量有点类似于高级语言中的指针,
它指向的是一块内存地址而不是具体的内容' 因此声明一个游标变量其实就是创建了一个
指针, 而不是创建了具体的内容。 在PL/SQL中, 指针是使用 REF 作为前缀进行定义的,
因此游标变量类型就是REF CURSOR类型.

12.3.2 声明游标变量类型

它的定义与集合或记录一样, 必须通过两个声明步骤来实现一个游标变量的
定义。
(l) 创建一个游标变量类型。
(2) 基于该类型创建实际的游标变量。

游标类型的声明语法如下所示。

TYPE cursor_type_name IS REF CURSOR [RETURN return_type ] ;

语法说明

  • TYPE语句指定将要定义]一个类型,关键字REFCURSOR表示将定义一个游标变量类
    型, REF 表示为一个指针类型, 因此理解起来就是表示指向游标的指针。 cursor_type_name
    是游标类型的名称, RETURN指定游标将要返回的类型,要求是一个由%ROWTYPE属性
    定义的记录或者是在定义游标类型之前预先定义的记录类型。
  • RETURN 子句是可选的y 当指定了 RETURN 子句后, 游标变量是受约束的, 要求游
    标必须具有特定的返回类型, 因此查询的选择列表必须匹配游标的返回类型′ 否则将出现
    预定义的 ROWTYPE_MISMATCH 异常。 如果不指定 RETURN 子句, 则游标变量是无约
    束的, 当后来打开一个无约束的游标变量时, 可以为任何查询而打开。
    注意:9i以上SYS_REFCURSOR 代替了gen_type

定义一个无约束的游标变量代码举例

TYPE emp__type IS REF CURSOR RETURN emp%ROWTYPE;
TYPE SYS_REFCURSOR IS REF CURSOR;

12.3.3 定义游标变量

语法

cursor_name cursor_type_name;

语法说明

cursor_name 是符合 PL/SQL 标识符命名规范的游标变量的名称, cursor_type_name是
预先使用 TYPE语句定义的游标类型

游标和游标变量的区别

12.3.4 打开游标变量

语法

OPEN cursor_name FOR select_statement;

语法说明

  • cursor_name是所要打开的游标的名称,select_statement是要进行查询的SELECT语句。
    由于仅在游标打开时,才 会为游标变量分配一个具体的游标对象, 因此 SELECT 语句的字
    段列表的数据类型必须要与游标类型定义时的 RETURN子句相匹配或者相兼容
  • 当用不同的查询语句打开同一个游标变量的时候, 上一个查询将被丢弃掉。

12.3.5 控制游标变量

语法

FETCH cursor_variab1e_name INTO record_name;
EETCHcursor_variab1e_name  INTO variab1e_name,variab1e_name . . . ;

语法说明

cursor_variab1e_name 是要提取的已打开的游标变量, record_name 是要提取的目标记
录类型. 也可以将游标变量中的列值直接赋给由 variable_name 指定的变量,使用 FETCH
语句提取游标变量数据.

12.3.6 处理游标变量异常

捕获INVALID_CURSOR,游标未打开异常即可
捕获ROWTYPE_MISMATCH,声明类型不一致异常

12.3.7 在包中使用游标变量

可以移至ORACLE PL/SQL从入门到精通笔记-包

12.3.8 游标变量的限制

  • 不能在包中声明游标变量。
  • 不能在创建表或创建视图的语句中把字段类型指定为 REFCURSOR类型, 数据库
    字段是不能存放游标变量值的。
  • 游标类型的参数不支持使用远程过程调用 (RPC) 将游标变量从一个服务器传递
    到另一个服务器。
  • 不能用比较操作符来判断两个游标变量是否相等、 不相等或者为 NULL。
  • 不能为游标变量赋空值.
  • 不能将REFCURSOR类型作为集合的元素类型,也就是说在索引表、 嵌套表和变
    长数组中不能存放游标变量的值.
  • 不能将在游标中使用的游标FOR循环用在游标变量上, 也就是说游标和游标变量
    不要试图互相替换.

12.3 游标变量涉及到的知识点代码如下:

DECLARE
  TYPE emp_type IS REF CURSOR RETURN hyz_emp%ROWTYPE; --定义游标的类型
  TYPE SYS_REFCURSOR IS REF CURSOR; --声明游标变量
  emp_cur emp_type; --emp_cur定义为emp_type类型
  gen_cur emp_type; --gen_cur定义为SYS_REFCURSOR类型
  v_emp   hyz_emp%ROWTYPE; --定义员工号
BEGIN
  --打开游标变量的表emp_type的字段类型必须和hyz_emp表字段类型完全一致
  OPEN emp_cur FOR
    SELECT * FROM hyz_emp s WHERE s.deptno = 30;
  --多次次后打开同一个emp_cur时,前面定义的emp_cur会被丢弃
  OPEN emp_cur FOR
    SELECT * FROM hyz_emp s WHERE s.deptno = 20;
  LOOP
    FETCH emp_cur
      INTO v_emp;
    EXIT WHEN emp_cur%NOTFOUND;
    dbms_output.put_line('用户id为:' || v_emp.empno || '姓名为:' || v_emp.ename);
  END LOOP;
  CLOSE emp_cur;
END;

本人是一枚程序猿,如果觉得整理的不错,请关注个人微信公众号(扫一扫):

猜你喜欢

转载自blog.csdn.net/huyingzuo/article/details/80117068
今日推荐