[oracle]引用游标

一.注意事项

1.创建游标变量需要两个步骤:

(1).声明ref游标类型
(2).声明ref游标类型的变量

2.cursor与ref cursor区别:

(1).cursor定义好后无法修改,ref cursor截图根据逻辑来动态打开。
(2).cursor不能返回给客户端,ref cursor可以。
(3).cursor可以使全局的global,ref cursor则必须定义在过程或函数中。
(4).cursor不能再子程序间传递,ref cursor可以。
(5).cursor中定义的静态sql比ref cursor效率高,所以ref cursor通常用于向客户端返回结果集。

以下通过例子来简单上手引用游标

准备数据

创建两张表,一张是学生表,一张是年级表。

create sequence studentTest_id_seq
increment by 1
start with 1
minvalue 1
maxvalue 999999999;
 
create table studentTest(
       id number(9) not null primary key,--编号
       name varchar(20) not null,--姓名
       classNo number(3) not null,--班级编号
       sex varchar(2) not null,--性别
       score number(5,2) not null,--分数
       birthday date,--出生日期
       address varchar(100)--地址
);
 
insert into studentTest values(studentTest_id_seq.Nextval,'黄小花',101,'女',95,to_date('2011-01-01','yyyy-MM-dd'),'北京高新区');
insert into studentTest values(studentTest_id_seq.Nextval,'李大傻',101,'男',33,to_date('2010-10-01','yyyy-MM-dd'),null);
insert into studentTest values(studentTest_id_seq.Nextval,'张翠花',102,'女',79,null,'桂林欧家村');
insert into studentTest values(studentTest_id_seq.Nextval,'王二狗',102,'男',82,to_date('2009-02-25','yyyy-MM-dd'),'桂林秀峰区');
insert into studentTest values(studentTest_id_seq.Nextval,'刘蓉蓉',101,'女',46,to_date('2010-08-20','yyyy-MM-dd'),'广东深圳');
insert into studentTest values(studentTest_id_seq.Nextval,'陈大胖',102,'男',27,to_date('2011-04-30','yyyy-MM-dd'),'天津塘沽');
commit;
 
create table classTest
(
  id NUMBER(5) not null primary key,--班级编号
  name VARCHAR2(50) not null,--班级名称
  place VARCHAR2(50)--班级位置
);
 
insert into classTest (ID, NAME, PLACE) values (101, '普通1班', 'C栋教学楼305');
insert into classTest (ID, NAME, PLACE) values (102, '普通2班', 'B栋教学楼112');
insert into classTest (ID, NAME, PLACE) values (103, '尖子1班', '学霸楼606');
insert into classTest (ID, NAME, PLACE) values (104, '尖子2班', '学霸楼707');
insert into classTest (ID, NAME, PLACE) values (105, '尖子3班', '学霸楼808');
commit;

例子1.简单引用游标(弱类型游标)

相比显式游标,定义时不指定查询语句,打开时指定查询语句。一般也是有固定的四个步骤:声明游标 、打开游标 、提取数据 、关闭游标。

create or replace procedure procTest1(choice int)
as
  type refType is ref cursor;--1.(1).定义游标类型 :相当于声明一个引用游标的类,类名为refType
  cur_student refType;--1.(2).声明游标:给refType这个类声明一个对象,叫cur_student
  row_student studentTest%rowtype;
  row_class classTest%rowtype;
begin
  if choice=1 then
    open cur_student for select * from studentTest;--2.打开游标
    loop
      fetch cur_student into row_student;--3.提取数据
      exit when cur_student%notfound;
      dbms_output.put_line('id=' || row_student.id || ',姓名=' || row_student.name ||',班级=' || row_student.classno || ',性别=' ||row_student.sex || ',分数=' || row_student.score ||',出生日期=' ||to_char(row_student.birthday, 'yyyy-MM-dd') || ',地址=' ||row_student.address);
    end loop;
    close cur_student;--4.关闭游标
    dbms_output.put_line('游标使用完毕');
  elsif choice=2 then
    open cur_student for select * from classTest;--2.打开游标
    loop
      fetch cur_student into row_class;--3.提取数据
      exit when cur_student%notfound;
      dbms_output.put_line('班级id='||row_class.id||',班级名称='||row_class.name||',班级位置='||row_class.place);
    end loop;
    close cur_student;--4.关闭游标
    dbms_output.put_line('游标使用完毕');
  else
    dbms_output.put_line('选择错误!');
  end if;
end;
 
call procTest1(1);

output输出结果为

id=1,姓名=黄小花,班级=101,性别=女,分数=95,出生日期=2011-01-01,地址=北京高新区
id=2,姓名=李大傻,班级=101,性别=男,分数=33,出生日期=2010-10-01,地址=
id=3,姓名=张翠花,班级=102,性别=女,分数=79,出生日期=,地址=桂林欧家村
id=4,姓名=王二狗,班级=102,性别=男,分数=82,出生日期=2009-02-25,地址=桂林秀峰区
id=5,姓名=刘蓉蓉,班级=101,性别=女,分数=46,出生日期=2010-08-20,地址=广东深圳
id=6,姓名=陈大胖,班级=102,性别=男,分数=27,出生日期=2011-04-30,地址=天津塘沽
游标使用完毕
call procTest1(2);

output输出结果为

班级id=101,班级名称=普通1班,班级位置=C栋教学楼305
班级id=102,班级名称=普通2班,班级位置=B栋教学楼112
班级id=103,班级名称=尖子1班,班级位置=学霸楼606
班级id=104,班级名称=尖子2班,班级位置=学霸楼707
班级id=105,班级名称=尖子3班,班级位置=学霸楼808
游标使用完毕

上面的例子1是弱类型游标,那什么是弱类型游标呢?看它和下面强类型游标的对比就知道了。

例子2.强类型游标

定义游标类型的时候指定了返回值

create or replace procedure procTest1_2(choice int)
as
  type refType is ref cursor return studentTest%rowtype;--相比弱类型游标,多了个返回值,相当于指定了游标只能返回表studentTest的rowtype类型。
  cur_student refType;
  row_student studentTest%rowtype;
  row_class classTest%rowtype;
begin
  if choice=1 then
    open cur_student for select * from studentTest;--符合游标返回值类型
    loop
      fetch cur_student into row_student;
      exit when cur_student%notfound;
      dbms_output.put_line('id=' || row_student.id || ',姓名=' || row_student.name ||',班级=' || row_student.classno || ',性别=' ||row_student.sex || ',分数=' || row_student.score ||',出生日期=' ||to_char(row_student.birthday, 'yyyy-MM-dd') || ',地址=' ||row_student.address);
    end loop;
    close cur_student;
    dbms_output.put_line('游标使用完毕');
  elsif choice=2 then
    open cur_student for select * from classTest;--不符合游标返回值类型,所以会导致编译不通过。
    loop
      fetch cur_student into row_class;
      exit when cur_student%notfound;
      dbms_output.put_line('班级id='||row_class.id||',班级名称='||row_class.name||',班级位置='||row_class.place);
    end loop;
    close cur_student;
    dbms_output.put_line('游标使用完毕');
  else
    dbms_output.put_line('选择错误!');
  end if;
end;
 
call procTest1_2(1);--这个存储过程因为编译不通过,所以这一句代码执行不了。

可以看一下存储过程的编译出错信息
在这里插入图片描述

例子3.使用游标变量执行动态sql(即ref游标如何传参)
(1).单个参数:查询指定性别的学生
create or replace procedure procTest2(ssex varchar2)
as
       type refType is ref cursor;
       cur_student refType;
       row_student studentTest%rowtype;
       mysql varchar2(100);
begin
  mysql:='select * from studentTest where sex=:1';--占位符写成":序号"形式
  open cur_student for mysql using ssex;--using后面接参数列表
  loop
    fetch cur_student into row_student;
    exit when cur_student%notfound;
    dbms_output.put_line('id=' || row_student.id || ',姓名=' || row_student.name ||',班级=' || row_student.classno || ',性别=' ||row_student.sex || ',分数=' || row_student.score ||',出生日期=' ||to_char(row_student.birthday, 'yyyy-MM-dd') || ',地址=' ||row_student.address);
  end loop;
  close cur_student;
  dbms_output.put_line('游标使用完毕');
end;
 
call procTest2('男');

output输出结果为

id=2,姓名=李大傻,班级=101,性别=男,分数=33,出生日期=2010-10-01,地址=
id=4,姓名=王二狗,班级=102,性别=男,分数=82,出生日期=2009-02-25,地址=桂林秀峰区
id=6,姓名=陈大胖,班级=102,性别=男,分数=27,出生日期=2011-04-30,地址=天津塘沽
游标使用完毕
call procTest2('女');

output输出结果为

id=1,姓名=黄小花,班级=101,性别=女,分数=95,出生日期=2011-01-01,地址=北京高新区
id=3,姓名=张翠花,班级=102,性别=女,分数=79,出生日期=,地址=桂林欧家村
id=5,姓名=刘蓉蓉,班级=101,性别=女,分数=46,出生日期=2010-08-20,地址=广东深圳
游标使用完毕
(2).多个参数以及模糊查询
create or replace procedure procTest3(sname varchar2,ssex varchar2)
as
       type refType is ref cursor;
       cur_student refType;
       row_student studentTest%rowtype;
       mysql varchar2(200);
begin
  mysql:='select * from studentTest where name like :1 and sex=:2';
  open cur_student for mysql using '%'||sname||'%',ssex;--参数列表:多个参数时按顺序用逗号拼接
  loop
    fetch cur_student into row_student;
    exit when cur_student%notfound;
    dbms_output.put_line('id=' || row_student.id || ',姓名=' || row_student.name ||',班级=' || row_student.classno || ',性别=' ||row_student.sex || ',分数=' || row_student.score ||',出生日期=' ||to_char(row_student.birthday, 'yyyy-MM-dd') || ',地址=' ||row_student.address);
  end loop;
  close cur_student;
  dbms_output.put_line('游标使用完毕');
end;

顺带一提:要使用模糊查询时下面这种写法会报错:绑定变量不存在。所以要用上面的那种写法。

mysql:='select * from studentTest where name like ''%:1%'' and sex=:2';--错误主要出在:1占位符处,:2占位符是没问题的。
open cur_student for mysql using sname,ssex;

查询名字带有“花”,且性别为“女”的学生。

call procTest3('花','女');

output输出结果为

id=1,姓名=黄小花,班级=101,性别=女,分数=95,出生日期=2011-01-01,地址=北京高新区
id=3,姓名=张翠花,班级=102,性别=女,分数=79,出生日期=,地址=桂林欧家村
游标使用完毕

查询名字带有“大”,且性别为“男”的学生。

call procTest3('大','男');

output输出结果为

id=2,姓名=李大傻,班级=101,性别=男,分数=33,出生日期=2010-10-01,地址=
id=6,姓名=陈大胖,班级=102,性别=男,分数=27,出生日期=2011-04-30,地址=天津塘沽
游标使用完毕
例子4.使用bulk collect批量提取

上述例子的游标基本都是通过loop一行一行的提取,而bulk collect可以一次性提取成列的集合,减少loop的开销。

create or replace procedure procTest4
as
       type refType is ref cursor;
       cur_class refType;
       type idList is table of classTest.id%type;--定义一个集合类
       ids idList;--声明一个集合类的对象
       type nameList is table of classTest.name%type;
       names nameList;
       type placeList is table of classTest.place%type;
       places placeList;
begin
  open cur_class for 'select c.id,c.name,c.place from classTest c';
  fetch cur_class bulk collect into ids,names,places;--提取成集合,这个提取是一次性提取,所以不能放在循环里。
  close cur_class;--可以直接关闭游标了
  for i in ids.first.. ids.last--i为集合中项的索引
  loop
    dbms_output.put_line('id='||ids(i)||',name='||names(i)||',place='||places(i));
  end loop;
  dbms_output.put_line('集合显示完毕');
end;
 
call procTest4();

output输出结果为

id=101,name=普通1班,place=C栋教学楼305
id=102,name=普通2班,place=B栋教学楼112
id=103,name=尖子1班,place=学霸楼606
id=104,name=尖子2班,place=学霸楼707
id=105,name=尖子3班,place=学霸楼808
集合显示完毕
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值