oracle中的游标

游标的概念: 

        游标是SQL的一个内存工作区,由系统或用户以变量的形式定义。游标的作用就是用于临时存储从数据库中提取的数据块。在某些情况下,需要把数据从存放在磁盘的表中调到计算机内存中进行处理,最后将处理结果显示出来或最终写回数据库。这样数据处理的速度才会提高,否则频繁的磁盘数据交换会降低效率。
       游标有两种类型:显式游标和隐式游标。在前述程序中用到的SELECT...INTO...查询语句,一次只能从数据库中提取一行数据,对于这种形式的查询和DML操作,系统都会使用一个隐式游标。但是如果要提取多行数据,就要由程序员定义一个显式游标,并通过与游标有关的语句进行处理。显式游标对应一个返回结果为多行多列的SELECT语句。
       游标一旦打开,数据就从数据库中传送到游标变量中,然后应用程序再从游标变量中分解出需要的数据,并进行处理。

一、隐式游标
如前所述,DML操作和单行SELECT语句会使用隐式游标,它们是:
*插入操作:INSERT。
*更新操作:UPDATE。
*删除操作:DELETE。
*单行查询操作:SELECT ... INTO ...。
当系统使用一个隐式游标时,可以通过隐式游标的属性来了解操作的状态和结果,进而控制程序的流程。隐式游标可以使用名字SQL来访问,但要注意,通过SQL游标名总是只能访问前一个DML操作或单行SELECT操作的游标属性。所以通常在刚刚执行完操作之后,立即使用SQL游标名来访问属性。
游标的属性有四种,如下所示。


隐式游标的属性               返回值类型                         意 义
SQL%ROWCOUNT        整型                      代表DML语句成功执行的数据行数
SQL%FOUND                 布尔型                  值为TRUE代表插入、删除、更新或单行查询操作成功
SQL%NOTFOUND         布尔型                  与SQL%FOUND属性返回值相反
SQL%ISOPEN                布尔型                  DML执行过程中为真,结束后为假


【训练1】 使用隐式游标的属性,判断对雇员工资的修改是否成功。


步骤1:输入和运行以下程序:
SET SERVEROUTPUT ON
BEGIN
       UPDATE emp SET sal=sal+100 WHERE empno=1234;
       IF SQL%FOUND THEN
              DBMS_OUTPUT.PUT_LINE('成功修改雇员工资!');
              COMMIT;
       ELSE
              DBMS_OUTPUT.PUT_LINE('修改雇员工资失败!');
       END IF;
END;

运行结果为:
修改雇员工资失败!
PL/SQL 过程已成功完成。

步骤2:将雇员编号1234改为7788,重新执行以上程序:

运行结果为:
成功修改雇员工资!
PL/SQL 过程已成功完成。


说明:本例中,通过SQL%FOUND属性判断修改是否成功,并给出相应信息。

二、显式游标
游标的定义和操作
游标的使用分成以下4个步骤。
1.声明游标
在DECLEAR部分按以下格式声明游标:
CURSOR 游标名[(参数1 数据类型[,参数2 数据类型...])]
IS SELECT语句;
参数是可选部分,所定义的参数可以出现在SELECT语句的WHERE子句中。如果定义了参数,则必须在打开游标时传递相应的实际参数。
SELECT语句是对表或视图的查询语句,甚至也可以是联合查询。可以带WHERE条件、ORDER BY或GROUP BY等子句,但不能使用INTO子句。在SELECT语句中可以使用在定义游标之前定义的变量。
2.打开游标
在可执行部分,按以下格式打开游标:
OPEN 游标名[(实际参数1[,实际参数2...])];
打开游标时,SELECT语句的查询结果就被传送到了游标工作区。
3.提取数据
在可执行部分,按以下格式将游标工作区中的数据取到变量中。提取操作必须在打开游标之后进行。
FETCH 游标名 INTO 变量名1[,变量名2...];

FETCH 游标名 INTO 记录变量;
游标打开后有一个指针指向数据区,FETCH语句一次返回指针所指的一行数据,要返回多行需重复执行,可以使用循环语句来实现。控制循环可以通过判断游标的属性来进行。
下面对这两种格式进行说明:
第一种格式中的变量名是用来从游标中接收数据的变量,需要事先定义。变量的个数和类型应与SELECT语句中的字段变量的个数和类型一致。
第二种格式一次将一行数据取到记录变量中,需要使用%ROWTYPE事先定义记录变量,这种形式使用起来比较方便,不必分别定义和使用多个变量。
定义记录变量的方法如下:
变量名 表名|游标名%ROWTYPE;
其中的表必须存在,游标名也必须先定义。
4.关闭游标
CLOSE 游标名;
显式游标打开后,必须显式地关闭。游标一旦关闭,游标占用的资源就被释放,游标变成无效,必须重新打开才能使用。

以下是使用显式游标的一个简单练习。
【训练1】用游标提取emp表中7788雇员的名称和职务。


SET SERVEROUTPUT ON
DECLARE
    v_ename VARCHAR2(10);
    v_job VARCHAR2(10);
   CURSOR emp_cursor IS SELECT ename,job FROM emp WHERE empno=7788;
BEGIN
   OPEN emp_cursor;
   FETCH emp_cursor INTO v_ename,v_job;
  DBMS_OUTPUT.PUT_LINE(v_ename||','||v_job);
  CLOSE emp_cursor;
END;

执行结果为:
SCOTT,ANALYST
PL/SQL 过程已成功完成。
说明:该程序通过定义游标emp_cursor,提取并显示雇员7788的名称和职务。


作为对以上例子的改进,在以下训练中采用了记录变量。
【训练2】用游标提取emp表中7788雇员的姓名、职务和工资。


SET SERVEROUTPUT ON
DECLARE
      CURSOR emp_cursor IS SELECT ename,job,sal FROM emp WHERE empno=7788;
      emp_record emp_cursor%ROWTYPE;
BEGIN
     OPEN emp_cursor;
     FETCH emp_cursor INTO emp_record;
     DBMS_OUTPUT.PUT_LINE(emp_record.ename||','|| emp_record.job||','|| emp_record.sal);
     CLOSE emp_cursor;
END;

执行结果为:
SCOTT,ANALYST,3000
PL/SQL 过程已成功完成。
说明:实例中使用记录变量来接收数据,记录变量由游标变量定义,需要出现在游标定义之后。
注意:可通过以下形式获得记录变量的内容:
记录变量名.字段名。


【训练3】显示工资最高的前3名雇员的名称和工资。


SET SERVEROUTPUT ON
DECLARE
     V_ename VARCHAR2(10);
     V_sal NUMBER(5);
     CURSOR emp_cursor IS SELECT ename,sal FROM emp ORDER BY sal DESC;
BEGIN
     OPEN emp_cursor;
     FOR I IN 1..3 LOOP
          FETCH emp_cursor INTO v_ename,v_sal;
         DBMS_OUTPUT.PUT_LINE(v_ename||','||v_sal);
     END LOOP;
CLOSE emp_cursor;
END;

执行结果为:
KING,5000
SCOTT,3000
FORD,3000
PL/SQL 过程已成功完成。

说明:该程序在游标定义中使用了ORDER BY子句进行排序,并使用循环语句来提取多行数据。


游标循环
【训练1】使用特殊的FOR循环形式显示全部雇员的编号和名称。


SET SERVEROUTPUT ON;  
DECLARE
       CURSOR emp_cursor IS SELECT empno, ename FROM emp;
BEGIN
       FOR Emp_record IN emp_cursor LOOP
             DBMS_OUTPUT.PUT_LINE(Emp_record.empno|| Emp_record.ename);
       END LOOP;
END;

执行结果为:
7369SMITH
7499ALLEN
7521WARD
7566JONES
PL/SQL 过程已成功完成。

说明:可以看到该循环形式非常简单,隐含了记录变量的定义、游标的打开、提取和关闭过程。Emp_record为隐含定义的记录变量,循环的执行次数与游标取得的数据的行数相一致。


【训练2】另一种形式的游标循环。


SET SERVEROUTPUT ON;
BEGIN
     FOR re IN (SELECT ename FROM EMP) LOOP
         DBMS_OUTPUT.PUT_LINE(re.ename) ;
    END LOOP;
END;

执行结果为:
SMITH
ALLEN
WARD
JONES

说明:该种形式更为简单,省略了游标的定义,游标的SELECT查询语句在循环中直接出现。



显式游标属性
虽然可以使用前面的形式获得游标数据,但是在游标定义以后使用它的一些属性来进行结构控制是一种更为灵活的方法。显式游标的属性如下所示。


游标的属性                返回值类型                     意 义
%ROWCOUNT         整型                 获得FETCH语句返回的数据行数
%FOUND                  布尔型             最近的FETCH语句返回一行数据则为真,否则为假
%NOTFOUND          布尔型             与%FOUND属性返回值相反
%ISOPEN                 布尔型             游标已经打开时值为真,否则为假
可按照以下形式取得游标的属性:
游标名%属性


要判断游标emp_cursor是否处于打开状态,可以使用属性emp_cursor%ISOPEN。如果游标已经打开,则返回值为“真”,否则为“假”。具体可参照以下的训练。
【训练1】使用游标的属性练习。


SET SERVEROUTPUT ON
DECLARE
        V_ename VARCHAR2(10);
        CURSOR emp_cursor IS SELECT ename FROM emp;
BEGIN
        OPEN emp_cursor;
        IF emp_cursor%ISOPEN THEN
        LOOP
               FETCH emp_cursor INTO v_ename;
               EXIT WHEN emp_cursor%NOTFOUND;
               DBMS_OUTPUT.PUT_LINE(to_char(emp_cursor%ROWCOUNT)||'-'||v_ename);
        END LOOP;
        ELSE
                DBMS_OUTPUT.PUT_LINE('用户信息:游标没有打开!');
        END IF;
        CLOSE emp_cursor;
END;

执行结果为:
1-SMITH
2-ALLEN
3-WARD
PL/SQL 过程已成功完成。

说明:本例使用emp_cursor%ISOPEN判断游标是否打开;使用emp_cursor%ROWCOUNT获得到目前为止FETCH语句返回的数据行数并输出;使用循环来获取数据,在循环体中使用FETCH语句;使用emp_cursor%NOTFOUND判断FETCH语句是否成功执行,当FETCH语句失败时说明数据已经取完,退出循环。



游标参数的传递
【训练1】带参数的游标。


SET SERVEROUTPUT ON
DECLARE
        V_empno NUMBER(5);
        V_ename VARCHAR2(10);
        CURSOR emp_cursor(p_deptno NUMBER, p_job VARCHAR2) IS
       SELECT empno, ename FROM emp
        WHERE deptno = p_deptno AND job = p_job;
BEGIN
       OPEN emp_cursor(10, 'CLERK');
       LOOP
             FETCH emp_cursor INTO v_empno,v_ename;
             EXIT WHEN emp_cursor%NOTFOUND;
             DBMS_OUTPUT.PUT_LINE(v_empno||','||v_ename);
        END LOOP;
END;

执行结果为:
7934,MILLER
PL/SQL 过程已成功完成。

说明:游标emp_cursor定义了两个参数:p_deptno代表部门编号,p_job代表职务。语句OPEN emp_cursor(10, 'CLERK')传递了两个参数值给游标,即部门为10、职务为CLERK,所以游标查询的内容是部门10的职务为CLERK的雇员。循环部分用于显示查询的内容。



也可以通过变量向游标传递参数,但变量需要先于游标定义,并在游标打开之前赋值。对以上例子重新改动如下:
【训练2】通过变量传递参数给游标。


SET SERVEROUTPUT ON
DECLARE
        v_empno NUMBER(5);
        v_ename VARCHAR2(10);
        v_deptno NUMBER(5);
        v_job VARCHAR2(10);
        CURSOR emp_cursor IS
        SELECT empno, ename FROM emp
        WHERE deptno = v_deptno AND job = v_job;
BEGIN
        v_deptno:=10;
        v_job:='CLERK';
       OPEN emp_cursor;
       LOOP
            FETCH emp_cursor INTO v_empno,v_ename;
            EXIT WHEN emp_cursor%NOTFOUND;
           DBMS_OUTPUT.PUT_LINE(v_empno||','||v_ename);
        END LOOP;
END;

执行结果为:
7934,MILLER
PL/SQL 过程已成功完成。

说明:该程序与前一程序实现相同的功能。


动态SELECT语句和动态游标的用法
Oracle支持动态SELECT语句和动态游标,动态的方法大大扩展了程序设计的能力。
对于查询结果为一行的SELECT语句,可以用动态生成查询语句字符串的方法,在程序执行阶段临时地生成并执行,语法是:
execute immediate 查询语句字符串 into 变量1[,变量2...];


以下是一个动态生成SELECT语句的例子。
【训练1】动态SELECT查询。


SET SERVEROUTPUT ON
DECLARE
       str varchar2(100);
       v_ename varchar2(10);
begin
       str:='select ename from scott.emp where empno=7788';
       execute immediate str into v_ename;
       dbms_output.put_line(v_ename);
END;

执行结果为:
SCOTT
PL/SQL 过程已成功完成。

说明:SELECT...INTO...语句存放在STR字符串中,通过EXECUTE语句执行。


在变量声明部分定义的游标是静态的,不能在程序运行过程中修改。虽然可以通过参数传递来取得不同的数据,但还是有很大的局限性。通过采用动态游标,可以在程序运行阶段随时生成一个查询语句作为游标。要使用动态游标需要先定义一个游标类型,然后声明一个游标变量,游标对应的查询语句可以在程序的执行过程中动态地说明
定义游标类型的语句如下:
TYPE 游标类型名 REF CURSOR;
声明游标变量的语句如下:
游标变量名 游标类型名;
在可执行部分可以如下形式打开一个动态游标:
OPEN 游标变量名 FOR 查询语句字符串;
【训练2】按名字中包含的字母顺序分组显示雇员信息。


declare
     type cur_type is ref cursor;
     cur cur_type;
     rec scott.emp%rowtype;
     str varchar2(50);
     letter char:= 'A';
begin
    loop
       str:= 'select ename from emp where ename like ''%'||letter||'%''';
       open cur for str;
       dbms_output.put_line('包含字母'||letter||'的名字:');
       loop
           fetch cur into rec.ename;
           exit when cur%notfound;
           dbms_output.put_line(rec.ename);
       end loop;
       exit when letter='Z';
       letter:=chr(ascii(letter)+1);
   end loop;
end;

运行结果为:
包含字母A的名字:
ALLEN
WARD
MARTIN
BLAKE
CLARK
ADAMS
JAMES
包含字母B的名字:
BLAKE
包含字母C的名字:
CLARK
SCOTT

说明:使用了二重循环,在外循环体中,动态生成游标的SELECT语句,然后打开。通过语句letter:=chr(ascii(letter)+1)可获得字母表中的下一个字母。


转自:https://www.cnblogs.com/xiaoliu66007/p/7495753.html

参考博客:
[1]REF游标 https://www.cnblogs.com/bulrush/p/7743848.html
[2]ref游标 https://www.cnblogs.com/programmer-wind/archive/2011/09/06/2919587.html
[3]oracle中游标详细用法 https://www.cnblogs.com/guohu/p/11007352.html

[4]oracle sys_refcursor用法和ref cursor区别 https://www.cnblogs.com/kkxwze/p/11155783.html

原文地址:https://www.cnblogs.com/shujk/p/12497592.html