1.基础定义
游标本质上就是一块内存区域,由系统或用户以变量的形式定义,可以提高数据数据处理的效率。
2.工作机制
将查询出的多条记录统一放入内存中,游标再从内存中拿取数据
3.解决问题
可以解决一次性查询多条记录的问题,处理多行数据的事务经常使用游标来实现
4.游标属性
游标名%isopen:判断游标是否打开,值是布尔类型,默认为false
游标名%found:判断游标是否找到记录,默认为false,找到记录为true
游标名%notfound:判断游标是否找到记录,默认为false,没找到为true
游标名%rowcount:获取游标影响的记录数,默认值为0,若将查询到的数据放入内存中,但游标并没有从内存中取数据,此时值为0
5.分类
隐式游标:使用DML语句时会自动创建,自动声明,打开及关闭,名称为SQL,检查属性获取最近执行信息,隐式游标是无法进行操作的。
显示游标:手动创建,开启,关闭,从内存中提取数据
ref游标:动态游标,定义时不需要指定select语句,使用时需要指定select语句
6.隐式游标
创建一个类型转换函数
create or replace function bool2char(bool boolean) return varchar2 is
begin
if bool then
return 'TRUE';
else
return 'FALSE';
end if;
end;
布尔类型的值是无法直接被打印的,所以需要先转换类型
DECLARE
V_EMP EMP%ROWTYPE;
BEGIN
SELECT * INTO V_EMP FROM EMP WHERE ROWNUM<2;
DBMS_OUTPUT.PUT_LINE(bool2char(SQL%ISOPEN));
DBMS_OUTPUT.PUT_LINE(bool2char(SQL%FOUND));
DBMS_OUTPUT.PUT_LINE(bool2char(SQL%NOTFOUND));
DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);
END;
7.显示游标
声明游标:cursor 游标名 is select语句;
打开游标:open 游标名;
提取数据:fetch 游标名 into 变量;
关闭游标:close 游标名;
当提取到最后一条数据时,再次进行提取返回的结果仍然是最后一条数据,并不会以null值进行覆盖。
DECLARE
CURSOR CUR IS SELECT * FROM EMP WHERE ROWNUM<3;
V_EMP EMP%ROWTYPE;
BEGIN
OPEN CUR;
FETCH CUR INTO V_EMP;
DBMS_OUTPUT.PUT_LINE(V_EMP.ENAME);
FETCH CUR INTO V_EMP;
DBMS_OUTPUT.PUT_LINE(V_EMP.ENAME);
FETCH CUR INTO V_EMP;
DBMS_OUTPUT.PUT_LINE(V_EMP.ENAME);
CLOSE CUR;
END;
8.属性应用
DECLARE
CURSOR CUR IS SELECT * FROM EMP WHERE ROWNUM<3;
V_EMP EMP%ROWTYPE;
BEGIN
--在open(打开游标)之前
DBMS_OUTPUT.PUT_LINE(bool2char(CUR%ISOPEN));--FALSE
--DBMS_OUTPUT.PUT_LINE(bool2char(CUR%FOUND));--错误
--DBMS_OUTPUT.PUT_LINE(bool2char(CUR%NOTFOUND));--错误
--DBMS_OUTPUT.PUT_LINE(CUR%ROWCOUNT);--错误
OPEN CUR;
--打开游标之后
DBMS_OUTPUT.PUT_LINE(bool2char(CUR%ISOPEN));--true
DBMS_OUTPUT.PUT_LINE(bool2char(CUR%FOUND));--false,只是将游标打开而并没有对数据进行提取
DBMS_OUTPUT.PUT_LINE(bool2char(CUR%NOTFOUND));--FALSE
DBMS_OUTPUT.PUT_LINE(CUR%ROWCOUNT);--0
FETCH CUR INTO V_EMP;
--提取数据之后
DBMS_OUTPUT.PUT_LINE(bool2char(CUR%FOUND));--TRUE
DBMS_OUTPUT.PUT_LINE(bool2char(CUR%NOTFOUND));--FALSE
DBMS_OUTPUT.PUT_LINE(CUR%ROWCOUNT);--1
CLOSE CUR;
--关闭游标之后
DBMS_OUTPUT.PUT_LINE(bool2char(CUR%ISOPEN));--FALSE,游标已经关闭
--DBMS_OUTPUT.PUT_LINE(bool2char(CUR%FOUND));--错误
--DBMS_OUTPUT.PUT_LINE(bool2char(CUR%NOTFOUND));--错误
--DBMS_OUTPUT.PUT_LINE(CUR%ROWCOUNT);--错误
END;
9.游标原理
(1)定义游标时会分配一块内存区域,内存中并没有任何数据。
(2)打开内存,将结果集存入内存。
(3)打开游标后,内存中的指针会指向第一条数据。
(4)提取数据时,会提取指针指向的数据,此时指针会指向下一条数据,当提取玩最后一条数据时指针会指向并不存在任何数据的位置,当再次提取数据时found值会变为false,notfound值变为true。
(5)关闭游标是为了释放内存资源。
10.显式游标的应用
(1)循环遍历游标
--loop循环遍历游标
DECLARE
CURSOR CUR IS SELECT * FROM EMP ;
V_EMP EMP%ROWTYPE;
BEGIN
OPEN CUR;
LOOP
FETCH CUR INTO V_EMP;
EXIT WHEN CUR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(V_EMP.ENAME);
END LOOP;
CLOSE CUR;
END;
--while循环遍历游标
DECLARE
CURSOR CUR IS SELECT * FROM EMP ;
V_EMP EMP%ROWTYPE;
BEGIN
OPEN CUR;
FETCH CUR INTO V_EMP;
WHILE CUR%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(V_EMP.ENAME);
FETCH CUR INTO V_EMP;
END LOOP;
CLOSE CUR;
END;
在while循环开始之前就开始提取数据是为了让found生效,found的含义是找到数据为true,若没有提取数据,则while循环的表达式就一直为false,是进不去循环的。
--for循环遍历游标,进入for循环时会自动打开游标并自动提取数据,退出for循环时会自动关闭游标,所以并没有必要使用open,fetch,close取对游标进行操作。
DECLARE
CURSOR CUR IS SELECT * FROM EMP ;
BEGIN
FOR V_EMP IN CUR LOOP
DBMS_OUTPUT.PUT_LINE(V_EMP.ENAME);
END LOOP;
END;
(2)引用游标类型
declare
cursor cur is select * from emp;
v_emp cur%rowtype;
begin
open cur;
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line('编号:'||v_emp.empno||'姓名:'||v_emp.ename);
end loop;
close cur;
end;
(3)into多个变量
declare
cursor cur is select empno,ename from emp;
v_empno emp.empno%type;
v_ename emp.ename%type;
begin
open cur;
loop
fetch cur into v_empno,v_ename;
exit when cur%notfound;
dbms_output.put_line('编号:'||v_empno||'姓名:'||v_ename);
end loop;
close cur;
end;
(4)记录类型
declare
cursor cur is select empno,ename from emp;
type t_type is record(v_empno number(4),v_ename varchar2(10));
v_emp t_type;
begin
open cur;
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line('编号:'||v_emp.v_empno||'姓名:'||v_emp.v_ename);
end loop;
close cur;
end;
(5)游标带返回值
declare
cursor cur return emp%rowtype is select * from emp;
begin
for v_emp in cur loop
dbms_output.put_line('编号:'||v_emp.empno||'姓名:'||v_emp.ename);
end loop;
end;
(6)游标带参数(传参)
declare
cursor cur(v_deptno number) is select * from emp where deptno=v_deptno;
v_emp cur%rowtype;
begin
open cur(&部门编号);
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line('编号:'||v_emp.empno||'姓名:'||v_emp.ename);
end loop;
close cur;
end;
(7)可更新游标,select语句后加上for update,where current of 游标名,根据指针更新数据
declare
cursor cur is select * from emp for update;
begin
for v_emp in cur loop
if v_emp.comm is null then
update emp set comm=100 where current of cur;
end if;
end loop;
end;
11.ref游标
语法:
declare
--声明游标类型
type t_cur is ref cursor;
--声明游标
cur t_cur;
v_emp emp%rowtype;
begin
--打开游标
open cur for 'select 语句';--for后的select语句也可以不加单引号
--提取数据
fetch cur into v_emp;
dbms_output.put_line(v_emp.ename);
--关闭游标
close cur;
end;
12.ref游标应用
ref游标分为强类型和弱类型,强类型是有返回值的,二弱类型是没有返回值的
(1)游标没有返回值(弱类型)
这是错误的,游标在声明时没有指定具体的数据类型,v_emp引用游标类型。
declare
type t_cur is ref cursor;
cur t_cur;
v_emp emp%rowtype;
begin
open cur for select * from emp;
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line('姓名:'||v_emp.ename);
end loop;
close cur;
end;
declare
type t_cur is ref cursor;
cur t_cur;
v_emp cur%rowtype;
begin
open cur for select * from emp;
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line('姓名:'||v_emp.ename);
end loop;
close cur;
end;
(2)游标带返回类型(强类型)
declare
type t_cur is ref cursor return emp%rowtype;
cur t_cur;
v_emp cur%rowtype;
begin
open cur for select * from emp;
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line('姓名:'||v_emp.ename);
end loop;
close cur;
end;
ref游标为强类型,打开游标时不能将sql以字符串的形式存在。
declare
type t_cur is ref cursor return emp%rowtype;
cur t_cur;
v_emp cur%rowtype;
begin
open cur for 'select * from emp';
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line('姓名:'||v_emp.ename);
end loop;
close cur;
end;
(3)sql字符串提取出来
declare
type t_cur is ref cursor;
cur t_cur;
v_emp emp%rowtype;
v_sql varchar2(100):='select * from emp';
begin
v_sql:=v_sql||' where deptno=10';
dbms_output.put_line(v_sql);
open cur for v_sql;
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line('姓名:'||v_emp.ename);
end loop;
close cur;
end;
(4)using占位符
declare
type t_cur is ref cursor;
cur t_cur;
v_emp emp%rowtype;
v_sql varchar2(100):='select * from emp where deptno=:1';
begin
open cur for v_sql using &部门号;
loop
fetch cur into v_emp;
exit when cur%notfound;
dbms_output.put_line('姓名:'||v_emp.ename);
end loop;
close cur;
end;
(5)其他循环遍历
while循环
declare
type t_cur is ref cursor;
cur t_cur;
v_emp emp%rowtype;
begin
open cur for select * from emp;
fetch cur into v_emp;
while cur%found loop
dbms_output.put_line('姓名:'||v_emp.ename);
fetch cur into v_emp;
end loop;
close cur;
end;
for循环:不能用于ref游标
declare
type t_cur is ref cursor;
cur t_cur;
begin
for v_emp in cur loop
dbms_output.put_line('姓名:'||v_emp.ename);
end loop;
end;
(6)ref游标传参:不能带参数
declare
type t_cur is ref cursor;
cur t_cur;
v_emp emp%rowtype;
begin
open cur(10) for select * from emp where deptno=v_deptno;
fetch cur into v_emp;
dbms_output.put_line('姓名:'||v_emp.ename);
close cur;
end;