--------------------------------------------
游标――就是一个指向保存有多行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;
----------------------------------------------