19.PLSQL游标

--------------------------------------------

游标――就是一个指向保存有多行SQL查询结果集的工作区的句柄(指针)。


一、显式游标


1、打开游标

2、将游标的结果集取到PLSQL记录或者PLSQL变量,fetch

3、关闭游标


案例1:定义变量的方式使用游标

declare

 cursor csr_org is select h.hrc_descr,o.org_short_name   --定义游标

                     from org_tab o,hrc_tab h

                    where o.hrc_code=h.hrc_code

                    order by 2;  

 v_hrc_descr varchar2(20);        --定义变量

 v_org_short_name varchar2(30);

begin

 open csr_org;   --打开游标

 loop

   fetch csr_org into v_hrc_descr,v_org_short_name;  --取出游标指针所指的行(每次取一行)

   exit when (csr_org%notfound);   --游标循环退出的条件

   dbms_output.put_line(rpad(v_hrc_descr,23,' ')||' '||rpad(v_org_short_name,30,' '));

 end loop;  --结束循环

 close csr_org;    --关闭游标

exception when others then

 null;

end;


循环一次,游标指针下移一行


输出:

CEO/COO                 Office of CEO ABC Inc.        

CEO/COO                 Office of CEO DataPro Inc.    

CEO/COO                 Office of CEO XYZ Inc.        

VP                      Office of VP Mktg ABC Inc.    

VP                      Office of VP Sales ABC Inc.  

VP                      Office of VP Tech ABC Inc.  


练习:SCOOT用游标输出部门名、员工名、员工薪水。用游标循环。

----------------------------------------------------------------

命令行下要先打开输出才能有输出:


SQL>set serveroutput on   --打开输出选项


答案:

declare

    cursor csr_org is select h.dname,o.ename,o.sal

                         from emp o,dept h

                        where o.deptno=h.deptno

                        order by 2;

      v_hrc_descr varchar2(20);

      v_sal number(7,2);

      v_org_short_name varchar2(30);

   begin

    open csr_org;

    loop

      fetch csr_org into v_hrc_descr,v_org_short_name,v_sal;

      exit when (csr_org%notfound);

    dbms_output.put_line(rpad(v_hrc_descr,23,' ')||' '||rpad(v_org_short_name,30,' ')||' '||rpad(v_sal,30,' '));

   end loop;

    close csr_org;

 exception when others then

  null;

end;

----------------------------------------------------------------------

输出:

RESEARCH                ADAMS                          1100                          

SALES                   ALLEN                          1600                          

SALES                   BLAKE                          2850                          

ACCOUNTING              CLARK                          2450                          

RESEARCH                FORD                           3000                          

SALES                   JAMES                          950                          

RESEARCH                JONES                          2975                          

ACCOUNTING              KING                           5000                          

SALES                   MARTIN                         1250                          

ACCOUNTING              MILLER                         1300                          

RESEARCH                SCOTT                          3000                          

RESEARCH                SMITH                          800                          

SALES                   TURNER                         1500                          

SALES                   WARD                           1250                          


PL/SQL procedure successfully completed


案例2:定义行变量(记录record)的方式使用游标

declare

 cursor csr_org is select h.hrc_descr,o.org_short_name

                     from org_tab o,hrc_tab h

                    where o.hrc_code=h.hrc_code

                    order by 2;

 v_org_rec csr_org%rowtype;   --定义一个行变量v_org_rec

begin

 open csr_org;

 loop

   fetch csr_org into v_org_rec;

   exit when (csr_org%notfound);

   dbms_output.put_line(rpad(v_org_rec.hrc_descr,23,' ')||' '||rpad(v_org_rec.org_short_name,30,' '));

 end loop;

 close csr_org;

exception when others then

 null;

end;


练习:SCOTT用游标输出部门名、员工名、员工薪水。用游标循环。用行变量取值。


答案:--------------------------------------------------------------

declare

   cursor csr_org is select h.dname,o.ename,o.sal

                       from emp o,dept h

                        where o.deptno=h.deptno

                       order by 2;


    v_roll csr_org%rowtype;

  begin

    open csr_org;

    loop

      fetch csr_org into v_roll;

      exit when (csr_org%notfound);

      dbms_output.put_line(rpad(v_roll.dname,23,' ')||' '||rpad(v_roll.ename,30,' ')||' '||rpad(v_roll.sal,30,' '));

    end loop;

   close csr_org;

  exception when others then

    null;

  end;

------------------------------------------------  


案例3:用while loop方式改写上面的程序

declare

 cursor csr_org is select h.hrc_descr,o.org_short_name

                     from org_tab o,hrc_tab h

                    where o.hrc_code=h.hrc_code

                    order by 2;

 v_org_rec csr_org%rowtype;

begin

 open csr_org;

 fetch csr_org into v_org_rec;

 while (csr_org%found) loop

   dbms_output.put_line(rpad(v_org_rec.hrc_descr,23,' ')||' '||rpad(v_org_rec.org_short_name,30,' '));

   fetch csr_org into v_org_rec;

 end loop;

 close csr_org;

exception when others then

 null;

end;


练习:SCOTT用游标输出部门名、员工名、员工薪水。用游标循环。用行变量取值。用while loop方式


答案;----------------------------------------------------------------

declare

   cursor csr_org is select h.dname,o.ename,o.sal

                       from emp o,dept h

                        where o.deptno=h.deptno

                       order by 2;


    v_roll csr_org%rowtype;

  begin

    open csr_org;


      fetch csr_org into v_roll;  

     while (csr_org%found) loop

      dbms_output.put_line(rpad(v_roll.dname,23,' ')||' '||rpad(v_roll.ename,30,' ')||' '||rpad(v_roll.sal,30,' '));

      fetch csr_org into v_roll;

    end loop;

   close csr_org;

  exception when others then

    null;

  end;

-----------------------------------------------------------------------

案例4:用FOR LOOP方式改写上面的程序


declare

 cursor csr_org is select h.hrc_descr,o.org_short_name

                     from org_tab o,hrc_tab h

                    where o.hrc_code=h.hrc_code

                    order by 2;

begin

 for idx in csr_org loop

   dbms_output.put_line(rpad(idx.hrc_descr,23,' ')||' '||rpad(idx.org_short_name,30,' '));

 end loop;

exception when others then

 null;

end;


练习:SCOTT用游标输出部门名、员工名、员工薪水。用游标循环。用行变量取值。用for loop方式

-------------------------------------------------------------------------

declare

   cursor csr_emp is select h.dname,o.ename,o.sal

                       from emp o,dept h

                        where o.deptno=h.deptno

                       order by 2;

    begin

   for ax in csr_emp loop

             dbms_output.put_line(rpad(ax.dname,23,' ')||' '||rpad(ax.ename,30,' ')||' '||rpad(ax.sal,30,' '));

   end loop;

   exception when others then

  null;

  end;

----------------------------------------------------------------------

案例5:不声明游标直接使用

begin

 for idx in (select h.hrc_descr,o.org_short_name

                     from org_tab o,hrc_tab h

                    where o.hrc_code=h.hrc_code

                    order by 2) loop

   dbms_output.put_line(rpad(idx.hrc_descr,23,' ')||' '||rpad(idx.org_short_name,30,' '));

 end loop;

exception when others then

 null;

end;


练习:SCOTT用游标输出部门名、员工名、员工薪水。不声明游标。用行变量取值。用for loop方式




显式游标的属性


属性                        数据类型             含义

----------------------------------

游标的名字%notfound        boolean        true:fetch取值失败  false:fetch取值成功

游标的名字%found           boolean        false:fetch取值失败  true:fetch取值成功

游标的名字%isopen          boolean        true:表示游标已经打开  false:游标已经关闭了

游标的名字%rowcount        number         open游标之后,fetch游标的第一行之后,close之前,

                                            游标共计处理了多少行

-------------------------------------------------

案例6:判断游标打开和关闭


不能打开一个已经打开的游标

不能关闭一个已经关闭的游标


declare

 cursor csr_org is select h.hrc_descr,o.org_short_name

                     from org_tab o,hrc_tab h

                    where o.hrc_code=h.hrc_code

                    order by 2;

 v_org_rec csr_org%rowtype;

begin

 if not csr_org%isopen then

    open csr_org;

 end if;

 loop

   fetch csr_org into v_org_rec;

   exit when (csr_org%notfound);

   dbms_output.put_line(rpad(v_org_rec.hrc_descr,23,' ')||' '||rpad(v_org_rec.org_short_name,30,' '));

 end loop;

 if csr_org%isopen then

    close csr_org;

 end if;

exception when others then

 null;

end;

-----------------------------------------------------------------------

案例7:取到游标的行数

declare

 cursor csr_org is select h.hrc_descr,o.org_short_name

                     from org_tab o,hrc_tab h

                    where o.hrc_code=h.hrc_code

                    order by 2;

 num_total_rows number;

begin

 for idx in csr_org loop

   dbms_output.put_line(rpad(idx.hrc_descr,23,' ')||' '||rpad(idx.org_short_name,30,' '));

   num_total_rows:=csr_org%rowcount;

 end loop;

 dbms_output.put_line(to_char(num_total_rows));

exception when others then

 null;

end;


将输出语句存储到循环内部

declare

 cursor csr_org is select h.hrc_descr,o.org_short_name

                     from org_tab o,hrc_tab h

                    where o.hrc_code=h.hrc_code

                    order by 2;

 num_total_rows number;

begin

 for idx in csr_org loop

   dbms_output.put_line(rpad(idx.hrc_descr,23,' ')||' '||rpad(idx.org_short_name,30,' '));

   num_total_rows:=csr_org%rowcount;   --累计处理的行数

   dbms_output.put_line(to_char(num_total_rows));

 end loop;

exception when others then

 null;

end;


-----------------------------

参数化游标――显式游标带参数,为特定的参数返回数据集合

1、避免定义多个游标

2、避免多个游标作硬解析


案例1:LOOP方式参数化游标

declare

 cursor csr_org(p_hrc_code number) is

                    select h.hrc_descr,o.org_short_name

                     from org_tab o,hrc_tab h

                    where o.hrc_code=h.hrc_code

                      and o.hrc_code=p_hrc_code

                    order by 2;

 v_org_rec csr_org%rowtype;

 num_total_rows number;

begin

 for idx in (select hrc_code from hrc_tab) loop

 open csr_org(idx.hrc_code);

 loop

   fetch csr_org into v_org_rec;

   exit when(csr_org%notfound);

   dbms_output.put_line(rpad(v_org_rec.hrc_descr,23,' ')||' '||rpad(v_org_rec.org_short_name,23,' '));

 end loop;

 close csr_org;

 end loop;  

exception when others then

 null;

end;



练习:按照部门分别输出每个部门的员工的姓名、职位、部门名称、薪资,要求用参数化游标,每个部门输出的信息之间用--------隔开。代码中只出现一次open。

dbms_output.put_line('------------------------------');

---loop循环

declare

 cursor csr_org(p_deptno number) is

   select h.dname, o.ename, o.sal, o.job

     from emp o, dept h

    where o.deptno = h.deptno

      and o.deptno = p_deptno

    order by 2;

 v_roll   csr_org%rowtype;

 num_rows number;

begin

 for ax in (select distinct deptno from emp) loop

    dbms_output.put_line('------dept'||to_char(idx.deptno)||' is below:------');

    open csr_org(ax.deptno);

   --dbms_output.put_line('--===========================------');

   loop

     fetch csr_org

       into v_roll;

     exit when(csr_org%notfound);


     dbms_output.put_line(rpad(v_roll.dname, 20, ' ') || ' ' ||

                          rpad(v_roll.ename, 20, ' ') || ' ' ||

                          rpad(to_char(v_roll.sal), 20, ' ') || ' ' ||

                          rpad(v_roll.job, 20, ' '));

   end loop;

   close csr_org;

   dbms_output.put_line('----------------------------------------------');

 end loop;

exception

 when others then

   null;

end;

--------------------------------------------------------------------

------------------隐式游标---------------------


隐式游标――当一个DML执行的时候,PLSQL引擎会自动打开一个指向该工作区域的隐式游标,执行完毕之后,游标自动关闭。


隐式游标的属性


属性                                           数据类型                       含义

----------------------------------

SQL%notfound        boolean        true:fetch取值失败  false:fetch取值成功

SQL%found           boolean        false:fetch取值失败  true:fetch取值成功

SQL%isopen          boolean        true:表示游标已经打开  false:游标已经关闭了

SQL%rowcount        number         DML之后,commit之前,游标共计处理了多少行

-------------------------------------------------


一、使用隐式游标的属性

begin

 delete from sec_hrc_org_tab where hrc_code=1;

 if (sql%found) then

   dbms_output.put_line(to_char(sql%rowcount)||' rows delete table for sec_hrc_org_tab!');

 end if;   --隐式游标的属性一定要在commit之前取到,否则就取不到了。

 commit;

 insert into sec_hrc_org_tab select h.hrc_code,h.hrc_descr,o.org_id,o.org_short_name,o.org_long_name

                                from org_tab o,hrc_tab h

                               where o.hrc_code=h.hrc_code

                                 and h.hrc_code=1;

  if (sql%found) then

    dbms_output.put_line(to_char(sql%rowcount)||' rows insert table for sec_hrc_org_tab!');

  end if;

  commit;

exception when others then

 null;

end;


注意:

DML1;

DML2;

下面取到的属性是DML2的属性,因为DML1的属性已经被覆盖了!


如果提交了之后再去取属性值将取不到:

begin

 delete from sec_hrc_org_tab where hrc_code=1;

 if (sql%found) then

   dbms_output.put_line(to_char(sql%rowcount)||' rows delete table for sec_hrc_org_tab!');

 end if;

 commit;

 insert into sec_hrc_org_tab select h.hrc_code,h.hrc_descr,o.org_id,o.org_short_name,o.org_long_name

                                from org_tab o,hrc_tab h

                               where o.hrc_code=h.hrc_code

                                 and h.hrc_code=1;

  commit;

  if (sql%found) then

    dbms_output.put_line(to_char(sql%rowcount)||' rows insert table for sec_hrc_org_tab!');

  end if;

exception when others then

 null;

end;


输出:

3 rows delete table for sec_hrc_org_tab!


二、使用隐式游标的属性,通过第三方表监控主程序。


监控表

select * from sec_hrc_audit


SQL> desc sec_hrc_audit

Name                                      Null?    Type

----------------------------------------- -------- ----------------------------

HRC_CODE                                  NOT NULL NUMBER(4)  

NUM_ROWS                                  NOT NULL NUMBER(8)

-------------------------------------------------------------------

declare

 v_num_rows number;

 v_hrc_code number:=2;

begin

 delete from sec_hrc_org_tab where hrc_code=v_hrc_code;

 insert into sec_hrc_org_tab select h.hrc_code,h.hrc_descr,o.org_id,o.org_short_name,o.org_long_name

                                from org_tab o,hrc_tab h

                               where o.hrc_code=h.hrc_code

                                 and h.hrc_code=v_hrc_code;

 v_num_rows:=sql%rowcount;

 if (sql%found) then

   update sec_hrc_audit set num_rows=num_rows+v_num_rows where hrc_code=v_hrc_code;

   if (sql%notfound) then

     insert into sec_hrc_audit values(v_hrc_code,v_num_rows);

   end if;

 end if;

 commit;

exception when others then

 null;

end;


验证:select * from sec_hrc_audit

---------------------------------------------------------------

练习:创建一个表EMP_T(CAST方式,只要结构不要数据),这表转存EMP表的数据,创建一个监控表EMP_INFO_AUDIT(deptno,num_emp),统计每个部门的人数。将EMP的数据插入到EMP_T,往监控表EMP_INFO_AUDIT统计一次,统计到每个部门的人数。创建表的过程是单独的,插入数据和监控的过程是在程序中的。


create table EMP_T as select * from emp where 1=2;

create table EMP_INFO_AUDIT(deptno number,num_emp number);


declare

 v_num number:=0;

begin

 for idx in (select deptno from dept) loop

   insert into EMP_T select * from emp where deptno=idx.deptno;

   v_num:=sql%rowcount;

   if (sql%found) then

     update EMP_INFO_AUDIT set num_emp=v_num where deptno=idx.deptno;

     if (sql%notfound) then

       insert into EMP_INFO_AUDIT values(idx.deptno,v_num);

     end if;

   end if;

   commit;

 end loop;

exception when others then

 null;

end;

-----------------------------------------------------------------

修改计算每个部门的总薪水。


SQL> alter table EMP_INFO_AUDIT add sum_sal number;


Table altered.



declare

 cursor cur_dept is select deptno

                      from dept;

 v_num number:=0;

begin

 for idx in cur_dept loop

   insert into EMP_T select * from emp where deptno=idx.deptno;

   v_num:=sql%rowcount;

   if (sql%found) then

     update EMP_INFO_AUDIT set num_emp=v_num where deptno=idx.deptno;

     if (sql%notfound) then

       insert into EMP_INFO_AUDIT values(idx.deptno,v_num,null);

     end if;

     update emp_info_audit set sum_sal=(select sum(sal) from emp_t where deptno=idx.deptno) where deptno=idx.deptno;

   end if;

   commit;

 end loop;

exception when others then

 null;

end;




修改计算每个部门的平均薪水。


SQL> alter table EMP_INFO_AUDIT add avg_sal number;


Table altered.

-----------------

declare

 cursor cur_dept is select deptno

                      from dept;

 v_num number:=0;

begin

 for idx in cur_dept loop

   insert into EMP_T select * from emp where deptno=idx.deptno;

   v_num:=sql%rowcount;

   if (sql%found) then

     update EMP_INFO_AUDIT set num_emp=v_num where deptno=idx.deptno;

     if (sql%notfound) then

       insert into EMP_INFO_AUDIT values(idx.deptno,v_num,null,null);

     end if;

     update emp_info_audit set (sum_sal,avg_sal)=(select sum(sal),round(avg(sal)) from emp_t where deptno=idx.deptno) where deptno=idx.deptno;

   end if;

   commit;

 end loop;

exception when others then

 null;

end;



----------------------------------------------


你可能感兴趣的:(Cursor,记录,where,工作区)