PL/SQL 11g R2 —— 游标

来源:互联网 发布:linux 修改mysql参数 编辑:程序博客网 时间:2024/05/16 19:45

1、游标的概念

游标是SQL的一个内存工作区,由系统或用户以变量的形式定义。游标的作用就是用于临时存储从数据库中提取的数据块。在某些情况下,需要把数据从存放在磁盘的表中调到计算机内存中进行处理,最后将处理结果显示出来或最终写回数据库。这样数据处理的速度才会提高,否则频繁的磁盘数据交换会降低效率。

游标有两种类型:显式游标和隐式游标。在前述程序中用到的SELECT...INTO...查询语句,一次只能从数据库中提取一行数据,对于这种形式的查询和DML操作,系统都会使用一个隐式游标。但是如果要提取多行数据,就要由程序员定义一个显式游标,并通过与游标有关的语句进行处理。显式游标对应一个返回结果为多行多列的SELECT语句。

2、隐式游标

如前所述,DML操作和单行SELECT语句会使用隐式游标,它们是:
* 插入操作:INSERT。
* 更新操作:UPDATE。
* 删除操作:DELETE。
* 单行查询操作:SELECT ... INTO ...。

隐式游标由 PL/SQL自动定义、打开和关闭

当系统使用一个隐式游标时,可以通过隐式游标的属性来了解操作的状态和结果,进而控制程序的流程。隐式游标可以使用名字SQL来访问,但要注意,通过SQL游标名总是只能访问前一个DML操作或单行SELECT操作的游标属性。所以通常在刚刚执行完操作之后,立即使用SQL游标名来访问属性。游标的属性有四种,如下所示。

  1. 隐式游标的属性 返回值类型   意    义  
  2. SQL%ROWCOUNT    整型  代表DML语句成功执行的数据行数  
  3. SQL%FOUND   布尔型 值为TRUE代表插入、删除、更新或单行查询操作成功  
  4. SQL%NOTFOUND    布尔型 与SQL%FOUND属性返回值相反  
  5. SQL%ISOPEN  布尔型 DML执行过程中为真,结束后为假 
--隐示游标declare                                               flag char:='U';                                     begin                                                  update dept                                           set dname='研发部'                                  where deptno=50;                                   if SQL%NOTFOUND then                                   flag:='Z';                                         insert into dept(deptno, dname) values(50,'研发部'); end if;                                              if flag='U' then                                        dbms_output.put_line('记录已更新');               else                                                    dbms_output.put_line('记录已插入');               end if;                                              end;                                                 /   

3、显示游标

显示游标需要声明、打开、提取、关闭。

显式游标的属性如下所示。

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

set serveroutput on  --显示游标fetch...intodeclare   cursor emp_cursor is select emp.ename,emp.sal from emp;   v_ename emp.ename%type;   v_sal emp.sal%type;begin   open emp_cursor;     loop fetch emp_cursor into v_ename,v_sal;       exit when emp_cursor%notfound;       dbms_output.put_line(v_ename||':'||v_sal);     end loop;   close emp_cursor;end;/--显示游标fetch...bulk collect intodeclare   cursor emp_cursor is select emp.ename from emp;   type ename_table_type is table of varchar2(10);   ename_table ename_table_type;begin   open emp_cursor;     fetch emp_cursor bulk collect into ename_table;     for i in 1..ename_table.count loop       dbms_output.put_line(ename_table(i));     end loop;   close emp_cursor;end;/--游标使用记录变量,使用参数declare   cursor emp_cursor(no number) is select * from emp where emp.deptno=no;   emp_record emp_cursor%rowtype;begin   open emp_cursor(&no);     loop fetch emp_cursor into emp_record;       exit when emp_cursor%notfound;       dbms_output.put_line(emp_record.ename||'='||emp_record.sal);     end loop;   close emp_cursor;end;/--使用游标删除数据declare   cursor emp_cursor is select * from bonus where rownum<=10 for update nowait;   emp_record emp_cursor%rowtype;begin   open emp_cursor;     loop fetch emp_cursor into emp_record;       exit when emp_cursor%notfound;       delete from bonus where current of emp_cursor;     end loop;   close emp_cursor;   commit;end;/--游标使用for循环declare   cursor emp_cursor is select * from emp;begin   for emp_record in emp_cursor loop     dbms_output.put_line(emp_cursor%rowcount||':'||emp_record.ename);   end loop;end;/

4、动态游标

--动态游标(REF游标)declare   type emp_cursor_type is ref cursor;   emp_cursor emp_cursor_type;   emp_record emp%rowtype;begin   open emp_cursor for select * from emp;     loop fetch emp_cursor into emp_record;       exit when emp_cursor%notfound;       dbms_output.put_line(emp_record.ename||'='||emp_record.sal);     end loop;   close emp_cursor;end;/--动态游标(REF游标)declare                                                  type emp_type is ref cursor;                             cur emp_type;                                            name varchar2(20);                                       salary number(7,2);                                    begin                                                      open cur for 'select ename,sal from emp where job=:1'    using 'SALESMAN';                                       loop                                                         fetch cur into name,salary;                                 exit when cur%notfound;                                    dbms_output.put_line(name||':'||salary);                  end loop;                                                close cur;                                            end;       /

5、总结

Cursor与 Ref Cursor区别
从技术底层看,两者是相同的。普通plsql cursor在定义时是“静态”的。而Ref cursors可以动态打开。
Ref cursor根据逻辑动态打开;而游标cursor定义好了就无法修改了
ref cursor可以返回给客户端,cursor则不行。
cursor可以是全局的global ,ref cursor则必须定义在过程或函数中。
ref cursor可以在子程序间传递,cursor则不行。
cursor中定义的静态sql比ref cursor效率高,所以ref cursor通常用在:向客户端返回结果集。


6、游标示例

显示游标

15-1:使用标量变量接受游标数据
    DECLARE
      CURSOR emp_cursor IS
        SELECT ename,job,sal FROM emp WHERE deptno=&dno;
      v_ename emp.ename%TYPE;
      v_sal emp.sal%TYPE;
      v_job emp.job%TYPE;
    BEGIN
      OPEN emp_cursor;
      LOOP
        FETCH emp_cursor INTO v_ename,v_job,v_sal;
        EXIT WHEN emp_cursor%NOTFOUND;
        dbms_output.put_line('姓名:'||v_ename||',岗位:'||v_job||',工资:'||v_sal);
      END LOOP;
      CLOSE emp_cursor;
    END;
    /
    
15-2:使用PL/SQL记录接受游标数据
    DECLARE
      CURSOR emp_cursor IS
        SELECT ename,sal FROM emp ORDER BY sal DESC;
      emp_record emp_cursor%ROWTYPE;
    BEGIN
      OPEN emp_cursor;
      LOOP
        FETCH emp_cursor INTO emp_record;
        EXIT WHEN emp_cursor%NOTFOUND OR emp_cursor%ROWCOUNT>&n;
        dbms_output.put_line('姓名:'||emp_record.ename||
         ',工资:'||emp_record.sal);
      END LOOP;
      CLOSE emp_cursor;
    END;
    /
    
15-3:使用PL/SQL集合变量接受游标数据
    DECLARE
      CURSOR emp_cursor IS SELECT ename,sal FROM emp
        WHERE lower(job)=lower('&job');
      TYPE emp_table_type IS TABLE OF emp_cursor%ROWTYPE
        INDEX BY BINARY_INTEGER;
      emp_table emp_table_type;
      i INT;
    BEGIN
      OPEN emp_cursor;
      LOOP
        i:=emp_cursor%ROWCOUNT+1;
        FETCH emp_cursor INTO emp_table(i);
        EXIT WHEN emp_cursor%NOTFOUND;
        dbms_output.put_line('姓名:'||emp_table(i).ename||
          ',工资:'||emp_table(i).sal);
      END LOOP;
      CLOSE emp_cursor;
    END;
    /
    
15-4:在FOR循环中引用已定义游标
    DECLARE
      CURSOR emp_cursor IS SELECT ename,hiredate FROM emp
        ORDER BY hiredate DESC;
    BEGIN
      FOR emp_record IN emp_cursor LOOP
        dbms_output.put_line('姓名:'||emp_record.ename
          ||',工作日期:'||emp_record.hiredate);
        EXIT WHEN emp_cursor%ROWCOUNT=&n;
      END LOOP;
    END;
    /
    
15-5:在FOR循环中直接引用子查询
    BEGIN
      FOR emp_record IN (SELECT ename,hiredate,rownum FROM emp
        ORDER BY hiredate) LOOP
        dbms_output.put_line('姓名:'||emp_record.ename
         ||',工作日期:'||emp_record.hiredate);
        EXIT WHEN emp_record.rownum=&n;
      END LOOP;
    END;
    /
    
15-6:参数游标
    DECLARE
      CURSOR emp_cursor(dno NUMBER) IS
        SELECT ename,job FROM emp WHERE deptno=dno;
    BEGIN
      FOR emp_record IN emp_cursor(&dno) LOOP
        dbms_output.put_line('姓名:'||emp_record.ename
          ||',岗位:'||emp_record.job);
      END LOOP;
    END;
    /
    
15-7:更新游标行
    DECLARE
      CURSOR emp_cursor IS
        SELECT ename,sal,deptno FROM emp FOR UPDATE;
      dno INT:=&no;
    BEGIN
      FOR emp_record IN emp_cursor LOOP
        IF emp_record.deptno=dno THEN
           dbms_output.put_line('姓名:'||emp_record.ename
            ||',原工资:'||emp_record.sal);
           UPDATE emp SET sal=sal*1.1 WHERE CURRENT OF emp_cursor;
        END IF;
      END LOOP;
    END;
    /
    
15-8:删除游标行
    DECLARE
      CURSOR emp_cursor IS
        SELECT ename FROM emp FOR UPDATE;
      name VARCHAR2(10):=lower('&name');
    BEGIN
      FOR emp_record IN emp_cursor LOOP
        IF lower(emp_record.ename)=name THEN
           DELETE FROM emp WHERE CURRENT OF emp_cursor;
        ELSE
           dbms_output.put_line('姓名:'||emp_record.ename);
        END IF;
      END LOOP;
    END;
    /
    
15-9:OF子句在特定表上加共享锁(只在EMP表上加锁)
    DECLARE
      CURSOR emp_cursor IS
        SELECT a.dname,b.ename FROM dept a JOIN emp b
        ON a.deptno=b.deptno
        FOR UPDATE OF b.deptno;
      name VARCHAR2(10):=LOWER('&name');
    BEGIN
      FOR emp_record IN emp_cursor LOOP
        IF LOWER(emp_record.dname)=name THEN
          dbms_output.put_line('姓名:'||emp_record.ename);
          DELETE FROM emp WHERE CURRENT OF emp_cursor;
        END IF;
      END LOOP;
    END;
    /
    

动态游标

15-10:使用无返回类型的游标变量
    DECLARE
      TYPE ref_cursor_type IS REF CURSOR;
      ref_cursor ref_cursor_type;
      v1 NUMBER(6);
      v2 VARCHAR2(10);
    BEGIN
      OPEN ref_cursor FOR
        SELECT &col1 col1,&col2 col2 FROM &table WHERE &cond;
      LOOP
        FETCH ref_cursor INTO v1,v2;
        EXIT WHEN ref_cursor%NOTFOUND;
        dbms_output.put_line('col1='||v1||',col2='||v2);
      END LOOP;
      CLOSE ref_cursor;
    END;
    /
    
15-11:使用有返回类型的游标变量
    DECLARE
      TYPE emp_cursor_type IS REF CURSOR RETURN emp%ROWTYPE;
      emp_cursor emp_cursor_type;
      emp_record emp%ROWTYPE;
    BEGIN
      OPEN emp_cursor FOR SELECT * FROM emp
        WHERE deptno=&dno;
      LOOP
        FETCH emp_cursor INTO emp_record;
        EXIT WHEN emp_cursor%NOTFOUND;
        dbms_output.put_line('姓名:'||emp_record.ename||
          ',工资:'||emp_record.sal);
      END LOOP;
      CLOSE emp_cursor;
    END;
    /
    

批量提取

15-12:使用FETCH ... BULK COLLECT 提取所有数据
    DECLARE
      CURSOR emp_cursor IS
        SELECT * FROM emp WHERE LOWER(job)=LOWER('&job');
      TYPE emp_table_type IS TABLE OF emp%ROWTYPE;
      emp_table emp_table_type;
    BEGIN
      OPEN emp_cursor;
      FETCH emp_cursor BULK COLLECT INTO emp_table;
      CLOSE emp_cursor;
      FOR i IN 1.. emp_table.COUNT LOOP
        dbms_output.put_line('姓名:'||emp_table(i).ename
          ||',工资:'||emp_table(i).sal);
      END LOOP;
    END;
    /
    
15-13:使用LIMIT子句限制提取行数
    DECLARE
      CURSOR emp_cursor IS SELECT * FROM emp;
      TYPE emp_array_type IS VARRAY(5) OF emp%ROWTYPE;
      emp_array emp_array_type;
    BEGIN
      OPEN emp_cursor;
      LOOP
        FETCH emp_cursor BULK COLLECT INTO emp_array LIMIT &rows;
        FOR i IN 1..emp_array.COUNT LOOP
          dbms_output.put_line('姓名:'||emp_array(i).ename
            ||',工资:'||emp_array(i).sal);
        END LOOP;
        EXIT WHEN emp_cursor%NOTFOUND;
      END LOOP;
      CLOSE emp_cursor;
    END;
    /
    

嵌套游标    

15-14:
    DECLARE
      CURSOR dept_cursor(no NUMBER) IS
         SELECT a.dname,CURSOR(SELECT * FROM emp
         WHERE deptno=a.deptno)
         FROM dept a WHERE a.deptno=no;
      TYPE ref_cursor_type IS REF CURSOR;
      emp_cursor ref_cursor_type;
      emp_record emp%ROWTYPE;
      v_dname dept.dname%TYPE;
    BEGIN
      OPEN dept_cursor(&dno);
      LOOP
         FETCH dept_cursor INTO v_dname,emp_cursor;
         EXIT WHEN dept_cursor%NOTFOUND;
         dbms_output.put_line('部门名:'||v_dname);
         LOOP
           FETCH emp_cursor INTO emp_record;
           EXIT WHEN emp_cursor%NOTFOUND;
           dbms_output.put_line('----雇员名:'||emp_record.ename
            ||',岗位:'||emp_record.job);
         END LOOP;
      END LOOP;
      CLOSE dept_cursor;
    END;
    /


原创粉丝点击