• ORACLE学习之PL/SQL编程——使用游标


    目录

    • 阅读准备事项
    • 概述
    • 显式游标
      1. 使用显式游标步骤
      2. 显式游标属性
      3. 游标for循环
      4. 参数游标
      5. 更新或删除游标行
    • 游标变量
    • 使用批量提取
    • 使用cursor表达式
    • 练习试题

     

    阅读准备事项

      请先阅读本学习系列之“ORACLE学习系列注意事项”。

    CREATE TABLE emp_learn AS SELECT * FROM employees;
    CREATE TABLE dept_learn AS SELECT * FROM departments;

     

    概述

      当在PL/SQL块中执行DML和单行SELECT INTO语句时,oracle会分配隐含游标。为了处理SELECT语句返回的多行数据,需要使用显式游标。从oracle database 9开始,也可以使用select ...bulk collect into语句将多行数据存放到PL/SQL集合变量。

      这一节主要学习在PL/SQL块中使用显示游标的方法,学习目标为:

    • 学会使用显式游标、游标for循环和参数游标。
    • 学会使用游标更新或删除表行。
    • 学会使用游标变量。
    • 学会使用批量提取和cursor表达式。

     

    显式游标

      PL/SQL包含隐含游标和显式游标两种游标类型,其中隐含游标用于处理单行select into和DML语句,而显示游标则用于处理select语句返回的多行数据。

    • 使用显示游标步骤

       a)定义游标:用于指定游标对应的select语句。语法:

        CURSOR cursor_name IS select_statement;

       b)打开游标:用于执行所对应的select语句,并将行数据存放到游标结果集。

        OPEN cursor_name;

       c)提取数据:将结果集中的行数据存放到PL/SQL变量。

        FETCH cursor_name INTO variable1[variable2,...];

       d)关闭游标:用于释放游标结果集的数据。

        CLOSE cursor_name;

    • 显式游标属性

        包含%ISOPEN、%FOUND、%NOTFOUND和&ROWCOUNT四种。结合示例解释含义:

        a)%ISOPEN:该属性用于检测游标是否已经打开。如果已经打开,则返回true,否则返回false。

        b)%FOUND:检测游标结果集是否存在数据。如果存在,则返回true,否则返回false。

        c)%NOTFOUND:检测结果集是否未提取到数据。如果不存在数据,则返回true,否则返回false。

        d)%ROWCOUNT:返回已提取的实际行数。

        示例:

     1 DECLARE
     2    CURSOR emp_learn_cursor01 IS
     3       SELECT first_name,salary,department_id FROM emp_learn WHERE department_id=&dno;
     4    TYPE emp_table_type IS TABLE OF emp_learn_cursor01%ROWTYPE INDEX BY Binary_Integer;
     5    emp_table emp_table_type;
     6    i INT;
     7 BEGIN
     8    --OPEN emp_learn_cursor01;
     9    IF NOT emp_learn_cursor01%ISOPEN THEN
    10       OPEN emp_learn_cursor01;
    11       dbms_output.put_line('检测到游标未打开,现已打开');
    12    END IF;
    13    LOOP
    14       dbms_output.put_line('ROWCOUNT的值:'||emp_learn_cursor01%ROWCOUNT);
    15       i:=emp_learn_cursor01%ROWCOUNT+1;--让集合下标从1开始
    16       FETCH emp_learn_cursor01 INTO emp_table(i);
    17       --EXIT WHEN emp_learn_cursor01%NOTFOUND;
    18       IF emp_learn_cursor01%NOTFOUND THEN
    19          dbms_output.put_line('未提取到行');
    20          EXIT;
    21       ELSIF emp_learn_cursor01%FOUND THEN
    22          dbms_output.put_line('提取到'||emp_learn_cursor01%ROWCOUNT||'');
    23       END IF;
    24          dbms_output.put_line('姓名:'||emp_table(i).first_name||',部门:'||emp_table(i).department_id||',工资:'||emp_table(i).salary);
    25    END LOOP;
    26    CLOSE emp_learn_cursor01;
    使用游标步骤
    •  游标for循环

         此功能为简化处理显式游标数据处理,当使用游标for循环时,oracle会隐含的打开游标、提取数据并关闭游标。语法:

         FOR recorde_name IN cursor_name LOOP

           statements;

         END LOOP;

        cursor_name用于指定已定义的游标名,而recorde_name是基于游标隐含定义的记录变量。在执行循环之前,oracle会隐含的打开游标,并且每循环一次自动提取一行数据,在提取了所有数据之后自动退出循环并隐含的关闭游标。

      示例:

    DECLARE
       CURSOR emp_learn_cursor01 IS
          SELECT first_name,salary,department_id FROM emp_learn WHERE department_id=&dno;
    BEGIN
       FOR emp_recorde IN emp_learn_cursor01 LOOP
          dbms_output.put_line('提取到'||emp_learn_cursor01%ROWCOUNT||'');
          dbms_output.put_line('姓名:'||emp_recorde.first_name||',部门:'||emp_recorde.department_id||',工资:'||emp_recorde.salary);
       END LOOP;
    END;
    使用游标for循环简化数据处理
    • 参数游标

         参数游标是指带有参数的游标。通过使用参数游标,使用不同的参数值可以生成不同的游标结果集。定义参数游标的语法:

        CURSOR cursor_name(parameter_name data_type) IS select_statement;

        OPEN cursor_name(parameter_value);(当不以游标for循环方式使用时,需要以此方式打开)  

    • 更行或删除游标行 

         通过使用显式游标,不仅可以取得游标结果集的数据,而且可以更新或删除游标结果集的当前行。需要注意的是:当使游标更新或者删除数据时,定义游标必须带有for update子句以加行共享锁,并且在更新或者删除游标行时必须带有where current of子句。语法:

        CURSOR cursor_name IS select_statement FOR UPDATE [OF column_reference] [NOWAIT];

        UPDATE table_name SET column=... WHERE CURRENT OF cursor_name;

        DELETE FROM table_name WHERE CURRENT OF cursor_name; 

      示例:

     1 DECLARE
     2    --定义游标,寻找出名字相同,且相同数为输入的特定参数的雇员的名字
     3    CURSOR name_cursor(v_same_count NUMBER) IS
     4       SELECT first_name FROM emp_learn GROUP BY first_name HAVING COUNT(*)=v_same_count;
     5    --定义游标,提取特定名字的雇员信息,以便执行UD操作
     6    CURSOR emp_info_cursor(v_name VARCHAR2) IS
     7       SELECT employee_id,first_name,salary FROM emp_learn WHERE first_name=v_name ORDER BY employee_id DESC FOR UPDATE;
     8    v_temp_name emp_learn.first_name%TYPE;
     9    v_cnt INT:=&cnt;
    10    v_num INT;
    11 BEGIN
    12    SELECT COUNT(*) INTO v_num FROM(SELECT first_name FROM emp_learn GROUP BY first_name HAVING COUNT(*)=v_cnt);
    13    dbms_output.put_line('操作前,名字相同,且相同数为'||v_cnt||'的分组数为'||v_num);
    14    
    15    SAVEPOINT s1;
    16    FOR emp_name_record IN name_cursor(v_cnt) LOOP
    17       FOR emp_info_record IN emp_info_cursor(emp_name_record.first_name) LOOP
    18          dbms_output.put_line('开始-雇员:'||emp_info_record.employee_id||',姓名'||emp_info_record.first_name||'工资'||emp_info_record.salary);
    19          CASE emp_info_cursor%ROWCOUNT
    20             WHEN 1 THEN
    21                UPDATE emp_learn SET salary=salary*0.9 WHERE CURRENT OF emp_info_cursor;
    22             WHEN 2 THEN
    23                DELETE FROM emp_learn WHERE CURRENT OF emp_info_cursor;
    24             WHEN 3 THEN
    25                UPDATE emp_learn SET salary=salary*1.1 WHERE CURRENT OF emp_info_cursor;
    26             ELSE
    27                NULL;
    28           END CASE;
    29       END LOOP;
    30       FOR emp_info_record IN emp_info_cursor(emp_name_record.first_name) LOOP
    31           dbms_output.put_line('结束-雇员:'||emp_info_record.employee_id||',姓名'||emp_info_record.first_name||'工资'||emp_info_record.salary);
    32       END LOOP; 
    33    END LOOP;
    34    dbms_output.put_line('操作结束');
    35    
    36    SELECT COUNT(*) INTO v_num FROM(SELECT first_name FROM emp_learn GROUP BY first_name HAVING COUNT(*)=v_cnt);
    37    dbms_output.put_line('操作完成后,名字相同,且相同数为'||v_cnt||'的分组数为'||v_num);
    38    ROLLBACK TO s1;
    39 END;
    找出first_name相同数为3的雇员,将他们雇员号小的工资涨10%,大的降10%,中间的开除

     

    游标变量

      游标变量是基于REF CURSOR类型所定义的变量,它实际是指向内存地址指针。使用显式游标只能定义静态游标,而通过使用游标变量可以在打开游标时指定其所对应的select语句,从而实现动态游标。注意:不能在远程子程序中使用游标变量。当指定return子句时,那么在打开游标时select子句的返回结果必须与return子句指定的记录类型匹配。使用步骤:

       a)定义REF CURSOR类型和游标变量

        TYPE ref_type_name IS REF CURSOR [RETURN return_type];

        cursor_variable ref_type_name;

       b)打开游标变量,指定游标变量所对应的select语句。select语句被执行,数据存放到了游标结果集

        OPEN cursor_variable FOR select_statement;

       c)提取数据

        FETCH cursor_variable INTO variable1,variable2,...;

       d)关闭游标变量

        CLOSE cursor_variable;

      示例:

     1 DECLARE
     2    TYPE ref_cursor_type IS REF CURSOR RETURN emp_learn%ROWTYPE;
     3    emp_cursor ref_cursor_type;
     4    emp_record emp_cursor%ROWTYPE;
     5 BEGIN
     6    OPEN emp_cursor FOR
     7       SELECT * FROM emp_learn WHERE department_id=&dept;
     8    LOOP
     9       FETCH emp_cursor INTO emp_record;
    10       EXIT WHEN emp_cursor%NOTFOUND;
    11       dbms_output.put_line(emp_record.first_name||'-'||emp_record.department_id);
    12    END LOOP;
    13    CLOSE emp_cursor;
    14 END;
    使用有返回类型的游标变量

     

    使用批量提取

        此功能为oracle 9i的新功能,通过使用fetch ... bulk collect into可以从游标结果集中一次提取所有数据。也可以使用limit子句限制每次提取的行数。

       示例:

     1 DECLARE
     2    TYPE ref_cursor_type IS REF CURSOR;
     3    emp_ref_cursor ref_cursor_type;
     4    TYPE emp_table_type IS TABLE OF emp_learn%ROWTYPE;
     5    emp_table emp_table_type;
     6    TYPE emp_arrary_type IS VARRAY(10) OF emp_learn.first_name%TYPE;
     7    emp_name_array emp_arrary_type;
     8 BEGIN
     9    OPEN emp_ref_cursor FOR
    10       SELECT * FROM emp_learn WHERE department_id=&dept;
    11    FETCH emp_ref_cursor BULK COLLECT INTO emp_table;
    12    FOR i IN 1..emp_table.count LOOP
    13       dbms_output.put_line(emp_table(i).first_name||'-'||emp_table(i).department_id);
    14    END LOOP;
    15    CLOSE emp_ref_cursor;
    16    
    17    --使用limit
    18    OPEN emp_ref_cursor FOR
    19       SELECT first_name FROM emp_learn;
    20    FETCH emp_ref_cursor BULK COLLECT INTO emp_name_array LIMIT 5;
    21    CLOSE emp_ref_cursor;
    22    dbms_output.put_line('被限制后数组的长度为:'||emp_name_array.count);
    23 END;
    使用批量提取

     

    使用CURSOR表达式

       cursor表达式用于实现嵌套游标,也是oracle 9i的新特性。通过使用cursor表达式,可以在PL/SQL块中处理基于多张表的复杂关联数据。为了可以在PL/SQL块中取得嵌套游标的数据,需要定义游标变量和嵌套循环。语法:

       CURSOR(subquery)

       示例:

     1 DECLARE
     2    CURSOR dept_cursor(deptno NUMBER) IS
     3       SELECT department_name,CURSOR(SELECT first_name,salary FROM employees WHERE department_id=d.department_id) 
     4          FROM dept_learn d WHERE d.department_id=deptno;
     5    TYPE ref_cursor_type IS REF CURSOR;
     6    emp_cursor ref_cursor_type;
     7    v_dname dept_learn.department_name%TYPE;
     8    TYPE emp_record_type IS RECORD(
     9       first_name VARCHAR2(30),
    10       salary     NUMBER
    11    );
    12    emp_record emp_record_type;
    13 BEGIN
    14    OPEN dept_cursor(&dno);
    15    LOOP
    16       FETCH dept_cursor INTO v_dname,emp_cursor;
    17       EXIT WHEN dept_cursor%NOTFOUND;
    18       LOOP
    19          FETCH emp_cursor INTO emp_record;
    20          EXIT WHEN emp_cursor%NOTFOUND;
    21          dbms_output.put_line('部门名:'||v_dname||',雇员姓名:'||emp_record.first_name||',工资:'||emp_record.salary);
    22       END LOOP;
    23    END LOOP;
    24    CLOSE dept_cursor;
    25 END;
    根据部门号,输出部门名称,该部门的雇员姓名和工资
  • 相关阅读:
    Windows ETW 学习与使用三
    暗云Ⅳ对SATA磁盘MBR Hook探索
    msahci代码调试备份
    mimikatz使用命令记录
    Windows ETW 学习与使用一
    RabbitMQ 实现延迟队列
    Redis 脱坑指南
    浅析 ThreadLocal
    IDEA2020.2.3破解
    用友NC 模块 简写(瞎猜的)
  • 原文地址:https://www.cnblogs.com/shizhongxing/p/3281239.html
Copyright © 2020-2023  润新知