target
了解什么是游标
掌握显示游标的使用
掌握隐士游标的使用
1. 什么是游标
游标的使用可以让用户像操作数组一样操作査询出来的数据集,这使得使用PL/SQL编程 更加方便。
1.1 游标的概念
可以将游标(Cursor)形象地看成一个变动的光标。它实际上是一个指针,它在一段 Oracle存放数据査询结果集或数据操作结果集的内存中,这个指针可以指向结果集中的任何一 条记录,这样就可以得到它所指向的数据了。
初始时它指向首记录。这种模型很像编程语言中的数组。
可以简单地理解游标为指向结果集记录的指针,利用游标可以返回它当前指向的行记录 (只能返回一行记录)。如果要返回多行,那么需要不断地滚动游标,把想要的数据査询一遍。
1.2游标的种类
Oracle中游标分为静态游标和REF游标两类。其中,静态游标就像一个数据快照,打开游标后的结果集是对数据库数据的一个备份,数据不随着对表执行DML操作后而改变。从这个特性来说,结果集是静态的。
静态游标包含如下两种类型:
- 显式游标
- 隐式游标
2. 显示游标
2.1 语法
CURSOR cursor_name
[(parameter_name datatype,...))
IS select_statement;
【语法说明】
- CURSOR cursor_name:声明游标,cursor_name是游标的名称。
- parameter_name:参数名称。
- datatype:参数类型。
- select_statement:游标关联的SELECT语句,但该语句不能是SELECT...INTO...语句。
2.2 显式游标的属性
利用游标属性可以得到游标执行的相关信息。显式游标有以下4个属性:
- %ISOPEN:用于判断游标是否打开,如果已经打开则返回TRUE,如果游标未打开则 返回FALSE。
- %FOUND:此属性可用来检测行数据是否有效。如果有效属性返回TRUE,否则返回FALSE。
- %NOTFOUND:与%FOUND属性恰好相反,如果没有提取出数据则返回TRUE,否则返回FALSE。
- %ROWCOUNT:累计到当前为止使用FETCH提取数据的行数。
🌰:%ISOPEN的使用
declare
cursor product_isopen_cur is
select * from productinfo;
cur_product_cd productinfo%rowtype;
begin
if product_isopen_cur%isopen then
fetch product_isopen_cur into cur_product_cd;
dbms_output.put_line('产品名称' || cur_product_cd.productname || '产品价格:'|| cur_product_cd.productprice );
else
dbms_output.put_line('游标没有打开!');
end if;
end;
/
输出:
游标没有打开!
PL/SQL 过程已成功完成。
2.3 游标的使用步骤
(1) 声明游标
声明游标主要用来给游标命名并且使得游标关联一个査询。具体语句如下:
declare
cursor cursor_Name is select_statement;
(2) 打开游标
游标中任何对数据的操作都是建立在游标被打开的前提下。打开游标初始化了游标指针, 游标一旦打开,其结果集都是静态的。也就是说,结果集此时不会反映出数据库中对数据进行 的增加、删除、修改操作。具体语句如下:
OPEN cursor_Name
(3) 读取数据
读取数据要利用FETCH
语句完成,它可以把游标指向位置的记录放入到PL/SQL声明的变量当中。
它只能取出指针当前行的记录。
正常情况下,FETCH要和循环语句一起使用,这样指针会不断前进,直到某个条件不符合要求而退出。
使用FETCH时游标属性%ROWCOUNT
会不断累加。具体语句如下:
FETCH cursor_Name INTO Record_Name;
(4) 关闭游标
关闭某个名称的游标。此时释放资源,结果集中的数据将不能做任何操作。
🌰:创建一个简单的游标
declare
cursor pdct_cur
is select * from productinfo;
product_cd productinfo%rowtype;
begin
open pdct_cur;
fetch pdct_cur into product_cd;
dbms_output.put_line(product_cd.productid || '-' || product_cd.productname || '-' || product_cd.productprice);
close pdct_cur;
end;
/
输出:
1-华为Mate40Pro-7999
PL/SQL 过程已成功完成。
只输出了一条记录,也就是前面说到的游标指针默认指向第一条记录,若想获取其他记录,要用循环去移动指针,然后读取记录。
2.4 游标中的loop语句
通常游标提取的数据不会是一条,而是多条记录。这样就需要一个遍历结果集的方式--loop语句。
🌰:使用loop循环获取多条记录
declare
cursor product_loop_cur
is select productid,productname,productprice from productinfo
where productprice > 3000;
cur_productid productinfo.productid%type;
cur_productname productinfo.productname%type;
cur_productprice productinfo.productprice%type;
begin
open product_loop_cur;
loop
fetch product_loop_cur into cur_productid,cur_productname,cur_productprice;
exit when product_loop_cur%notfound;
dbms_output.put_line('产品id:' || cur_productid || ' 产品名称:' || cur_productname || ' 产品价格:' || cur_productprice);
end loop;
close product_loop_cur;
end;
/
输出:
产品id:1 产品名称:华为Mate40Pro 产品价格:7999
产品id:2 产品名称:华为P40Pro 产品价格:6999
产品id:4 产品名称:华为MatePadPro 产品价格:4999
产品id:5 产品名称:华为智慧屏 产品价格:8999
产品id:6 产品名称:华为智慧屏2代 产品价格:9999
产品id:7 产品名称:华为智慧屏2代 产品价格:9999
PL/SQL 过程已成功完成。
2.5 使用bulk collect和for语句的游标
游标中通常使用FETCH...INTO...语句提取数据,这种方式是单条数据提取,在数据量很大的情况下执行效率不是很理想。而FETCH...BULK COLLECT INTO语句可以批量提取数据,在数据量大的情况下它的执行效率比单条提取数据的高。
🌰:
declare
cursor product_collect_cur
is select * from productinfo;
type prot_table is table of productinfo%rowtype;
pdct_rd prot_table;
begin
open product_collect_cur;
loop
--从游标中提取数据,每次取2条
fetch product_collect_cur bulk collect into pdct_rd limit 2;
for i in 1..pdct_rd.count loop
dbms_output.put_line('产品id:' || pdct_rd(i).productid || ' 产品名称:' || pdct_rd(i).productname || ' 产品价格:' || pdct_rd(i).productprice);
end loop;
exit when product_collect_cur%notfound;
end loop;
close product_collect_cur;
end;
/
2.6 使用 CURSOR FOR LOOP
游标很多机会都是迭代结果集,在PL/SQL这个过程中可以使用更简单的方式实现, CURSOR FOR LOOP不需要特别的声明变量。
declare
cursor cfl is
select productname,productprice from productinfo
where productprice > 3000;
begin
--把游标里面的数据放到curcfl中,该类型是%rowtype
for curcfl in cfl loop
dbms_output.put_line('名称:' || curcfl.productname || ' 产品价格:' || curcfl.productprice);
end loop;
end;
/
输出:
名称:华为Mate40Pro 产品价格:7999
名称:华为P40Pro 产品价格:6999
名称:华为MatePadPro 产品价格:4999
名称:华为智慧屏 产品价格:8999
名称:华为智慧屏2代 产品价格:9999
名称:华为智慧屏2代 产品价格:9999
PL/SQL 过程已成功完成。
2.7 带参数的游标
在使用显式游标时是可以指定参数的,指定的参数包括参数的顺序和参数的类型。参数可 以传递给游标在査询中使用,这样就方便了用户根据不同的査询条件进行査询,也方便了游标 在存储过程中的使用。
declare
cur_productname productinfo.Productname%TYPE := '华为';
cur_productprice productinfo.Productprice%TYPE := 1200;
cur_prodrcd productinfo%ROWTYPE;
CURSOR pdct_parameter_cur (name VARCHAR,price NUMBER) IS
SELECT * FROM PRODUCTINFO
WHERE productname like name||'%' AND productprice > price;
begin
OPEN pdct_parameter_cur(cur_productname,cur_productprice);
Loop
FETCH pdct_parameter_cur INTO cur_prodrcd;
EXIT WHEN pdct_parameter_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('产品ID, ' || cur_prodrcd.productid || ' 产品名称: ' || cur_prodrcd.productname || ' 产品价格:'|| cur_prodrcd.productprice);
END LOOP;
CLOSE pdct_parameter_cur;
END;
/
输出:
产品ID, 1 产品名称: 华为Mate40Pro 产品价格:7999
产品ID, 2 产品名称: 华为P40Pro 产品价格:6999
产品ID, 3 产品名称: 华为荣耀10 产品价格:1999
产品ID, 4 产品名称: 华为MatePadPro 产品价格:4999
产品ID, 5 产品名称: 华为智慧屏 产品价格:8999
产品ID, 6 产品名称: 华为智慧屏2代 产品价格:9999
产品ID, 7 产品名称: 华为智慧屏2代 产品价格:9999
PL/SQL 过程已成功完成。
3. 隐式游标
隐式游标和显式游标有所差异,它虽然没有显式游标一样的可操作性,但在实际的工作当 中也经常用到。
3.1隐式游标的特点
每当运行SELECT或DML语句时,PL/SQL会打开一个隐式的游标。隐式游标不受用户的控制,这一点和显式游标有明显的不同。下面列出了隐式游标和显式游标的不同处:
- 隐式游标由PL/SQL自动管理,
- 隐式游标中的%ISOPEN属性永远返回FALSE
- 隐式游标的默认名称是SQL
- SELECT或DML操作产生隐式游标:
- 隐式游标的属性值始终是最新执行的SQL语句的。
DECLARE
cur_productname productinfo.Productname%TYPE;
cur_productprice productinfo.Productprice%TYPE;
BEGIN
SELECT productname, productprice INTO cur_productname,cur_productprice
FROM PRODUCTINFO
where productid = '1';
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('产品名称:' || cur_productname || ', 产品价格:' || cur_productprice);
END IF ;
END;
/
输出:
产品名称:华为Mate40Pro, 产品价格:7999
PL/SQL 过程已成功完成。
3.2 游标中使用异常处理
使用游标时,某些情况下得到的数据超出了控制范围,如果不加处理会出现脚本执行中断的情况。这种情况下,脚本开发者通常会使用异常处理来维护脚本的稳定性。
🌰:在游标中使用异常处理
DECLARE
cur_productname productinfo.Productname%TYPE;
cur_productprice productinfo.Productprice%TYPE;
BEGIN
SELECT productname, productprice INTO cur_productname,cur_productprice
FROM PRODUCTINFO
where productid ='1';
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('产品名称:' || cur_productname || ' ,产品价格:' || cur_productprice);
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('没有数据!');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('数据过多!');
END;
/
输出:
产品名称:华为Mate40Pro ,产品价格:7999
PL/SQL 过程已成功完成。
4. 游标案例
本案例涉及到两张表,分别是EMP(员工表)和DEPT(部门表)。
要求:
利用游标转换这两张表的数据,要求把月薪高于1500的,部门地址为“CHICAGO”和“NEW YORK”的员工放到EMP_TEMP表中,月薪低于2500的上调5%。
分析:
第一步:建表 EMP_TEMP
第二步:创建游标存储符合条件的数据。
第三步:把符合要求的数据放进新表
第四步:把工资上调
操作步骤:
建表
create table EMP_TEMP as
select * from emp where 1=0;
编写游标案例:
declare
cur_empno emp.empno%type;
cur_ename emp.ename%type;
cur_emp_cd emp%rowtype;
--创建游标存储符合条件的数据
cursor cur_emp is
select * from emp where sal>1500 and DEPTNO
in (select DEPTNO from dept where loc in ('CHICAGO','NEW YORK'));
begin
--把符合条件的数据存到表中
open cur_emp;
loop
fetch cur_emp into cur_emp_cd;
if cur_emp%FOUND then
insert into emp_temp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
values(cur_emp_cd.empno,cur_emp_cd.ename,cur_emp_cd.job,cur_emp_cd.mgr,cur_emp_cd.hiredate,cur_emp_cd.sal,cur_emp_cd.comm,cur_emp_cd.deptno);
else
dbms_output.put_line('已经取出所有数据!共' || cur_emp%rowcount || '条记录');
exit;
end if;
end loop;
commit;
close cur_emp;
--调整工资
update emp_temp
set sal = sal * 1.05
where sal < 2500;
dbms_output.put_line('工资调整完毕!共调整' || SQL%ROWCOUNT || '条记录');
commit;
end;
/
输出:
已经取出所有数据!共4条记录
工资调整完毕!共调整2条记录
PL/SQL 过程已成功完成。
习题
一、填空题
1.静态游标包含 ( )和( ) 两种类型。
2.通常使用( )命令遍历游标的数据集。
3.游标的使用步骤包括 ( )、( )、( )、( )
4.游标4个属性是 ( )、( )、( )、( )
二、选择题
- 如果游标没有被打开时就调用,会不会提示出错?( )
A.不会 B.会 - 下面有关隐式游标中的%ISOPEN属性,说法正确的是( ).
A. 隐式游标中的%ISOPEN属性永远返回FALSE
B. 隐式游标中的%ISOPEN属性可以被人为地控制