现在的位置: 首页 > 综合 > 正文

PL/SQL 11g R2 —— 游标

2013年06月05日 ⁄ 综合 ⁄ 共 8923字 ⁄ 字号 评论关闭
文章目录

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...into
declare 
  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 into
declare 
  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;
    /

抱歉!评论已关闭.