这是一个忙碌的季节,也是一个收获颇多的季节。在忙碌工作的同时,挤出些时间写一些小dome,其乐无穷。也许这是最后一篇博客了,但愿玛雅人是在和我们开玩笑,呵呵!在时间允许的情况下,顺便把它搬到博客,希望对一些新手有帮助,对于一个新手来说,这样的博客我觉得很有用。我在自学的时候在网上查找了很多资料,但很少有一份资料适合我的口味,而且一篇文章拷贝来拷贝去的,甚至看了好多篇一模一样的“原创”文章,大部分都是谈谈一些理论,没有具体的dome参考,真正动起手来还是挺有难度的,这一步我还是顺利的走过来了,^_^!
一、什么是游标?
游标的字面理解就是游动的光标,用数据库语言来描述就是映射在结果集中一行数据上的位置实体,有了游标,用户就可以访问结果集中的任意一行数据了,将游标放置到某行后,即可对改行数据进行操作。
二、什么时候需要使用游标?
游标可以存储结果集,当我们需要使用结果集中的数据时,直接从游标里面遍历获取。
三、游标都有哪些?
PL/SQL有两种类型的游标:隐式游标(Implicit Cursor)和显示游标(Explicit Cursor)。PL/SQL为所有的SQL数据库操作语句都声明了一个隐式游标,其中也包括只返回一条记录的查询。但是,对于返回多条记录的查询来说,必须声明显示游标。
四、游标的使用
1.声明
CURSOR cursor_name[(parameter[,parameter]...)][return return_type]IS select_statement;
声明游标时参数和返回类型可根据实际情况选择,是可选的。其中,return_type必须是记录或是数据表的行类型,例如,
DECLARE CURSOR c1 IS SELECT t.business_id , t.business_type FROM tbl_flow_work t;
CURSOR c2 RETURN tbl_flow_work%rowTYPE IS SELECT t.business_id , t.business_type FROM t bl_flow_work t where t.flow_work_id =1;
需要注意的是,PL/SQL不允许向前引用,所以必须要在其他语句引用游标之前声明它,声明时,除了命名之外,还需要把它和一个查询相关联起来。游标名是一个未声明的标识符,而不是PL/SQL变 量名,因此,不能把值直接赋给游标名,也不能在表达式中使用。但是,游标和变量有着同样的作用域和规则,例如,在循环体内定义的游标只能作用于循环内部,不能在循环体外引用它。游标的形式参数都必须是IN模式的,即参数只能用于传进值,而不能对其进行赋值。此外,不能给游标的参数添加NOT NULL约束。可以为游标参数初始化默认值,如,
CURSOR c1(width INTEGER DEFAULT 0)...
2.打开和关闭游标
游标只有在打开只有才能执行查询操作,OPEN语句会把满足查询条件的行锁住,执行OPEN语句不会选取出结果集中的记录,只有在使用FETCH语句的时候才能将数据读取出来,如,
open c(parameter);
loop fetch c into v_businessname;
dbms_output.put_line(v_businessname);
end loop;
close c;
在大多数需要使用显示游标的情况下,都可以用一个游标FOR循环来代替OPEN、FETCH和CLOSE语句,这样可以省去一些繁琐的代码。游标FOR隐式地声明了一个%ROWTYPE类型的记录作为它的 循环索引,打开游标,然后反复地执行把结果集中的行放到索引中去,最后在所有行都被处理完成后关闭游标。如,
for cfw in cursor_flowwork loop
v_busstype := cfw.bt;
dbms_output.put_line(v_busstype);
end loop;
五、实例演示
现有四个表,一个是香港业务表TBL_BUSINESS_A,该表有business_id和business_name两个字段, business_id是主键。一个大陆业务表TBL_BUSINESS_B,也有两个字段business_id和business_name, business_id是主键。还有一个表是待办表tbl_flow_work,该表记录的是香港表和大陆表的待办信息,有一个外 键business_id字段对应的是两个主业务表的business_id。最后一个表是报表临时表tbl_temp_report。具体结 构和测试数据如图:
为了方便浏览,以下才测数据,临时表的数据是动态生成的。
- tbl_business_a测试数据:
- BUSINESS_ID BUSINESS_NAME CREATEDATE
- 1 安利销售代表申请书 2012-12-5 12:00:00
- 2 经销商营业执照变更说明书 2012-12-5 13:01:00
- 3 安利优惠顾客申请书 2012-12-5 12:00:00
- 4 安利优惠顾客申请书 2012-12-5 12:00:00
-
-
- tbl_business_b测试数据:
- BUSINESS_ID BUSINESS_NAME CREATEDATE
- 1 销售代表新加入 2012-12-5 12:00:00
- 2 安利优惠顾客网上补交身份证 2012-12-5 12:00:00
- 3 IDIS档案资料 2012-12-5 12:00:00
-
- tbl_flow_work测试数据:
- FLOW_WORK_ID BUSINESS_ID BUSINESS_TYPE OPERATE_NODE STATUS CREATE_DATE
- 1 1 1 1 1 2012-12-5 12:00:00
- 2 2 1 2 1 2012-12-5 12:00:00
- 3 3 1 3 1 2012-12-5 12:00:00
- 4 3 1 4 1 2012-12-5 12:00:00
- 5 1 2 1 1 2012-12-5 12:00:00
- 6 2 2 2 1 2012-12-5 12:00:00
- 7 3 2 3 1 2012-12-5 12:00:00
- 8 3 2 4 1 2012-12-5 12:00:00
- 9 3 1 1 0 2012-12-5 12:00:00
- 10 3 2 1 0 2012-12-5 12:00:00
- 11 4 1 1 1 2012-12-5 12:00:00
-
- tbl_temp_report测试数据:
- BUSINESS_TYPE BUSINESS_NAME
- 香港业务 安利销售代表申请书
- 大陆业务 销售代表新加入
- 香港业务 经销商营业执照变更说明书
- 大陆业务 安利优惠顾客网上补交身份证
- 香港业务 安利优惠顾客申请书
- 大陆业务 IDIS档案资料
- 香港业务 安利优惠顾客申请书
1.需求一,获取某一条待办对应的业务类型的业务名称?
现在既然是在学习游标,那么就应该用游标知识来解决该问题。其实很简单,可以声明一个带输入参数和一个输出
参数的存储过程,输入参数我们可以假设为待办表的id,而输出参数当然就是业务名称了。接着根据传入的待办id查询出
业务id和业务类型,即business_id和business_type,并且声明一个游标来存储。然后继续声明两个查询两个主业务表的带参数的游标,参数是业务id,条件也就是根据传入的参数查询,查询字段为业务名称business_name。最后就是遍历游标获得结果了,遍历待办表的游标,从游标中取得业务类型和业务id,代入主业务表的游标,得到业务名称,将业务名称写入输出参数返回。存储过程语句如下:
- create or replace procedure pro_test(par_flowworkid in number,
- par_businessname out varchar2) as
- v_busstype varchar2(1);
- v_bussid number(20);
- v_businessname varchar2(200);
- i number(20);
-
-
- cursor cursor_flowwork is
- select t.business_id as bid, t.business_type as bt
- from tbl_flow_work t
- where t.flow_work_id = par_flowworkid;
-
- cursor cursor_a(temp_bussid number) is
- select tba.business_name as bn
- from tbl_business_a tba
- where tba.business_id = temp_bussid;
-
- cursor cursor_b(temp_bussid number) is
- select tbb.business_name as bn
- from tbl_business_b tbb
- where tbb.business_id = temp_bussid;
- begin
- i := 1;
- for cfw in cursor_flowwork loop
- v_busstype := cfw.bt;
- v_bussid := cfw.bid;
- if v_busstype = '1' then
- open cursor_a(v_bussid);
- loop
- fetch cursor_a
- into v_businessname;
- exit when cursor_a%notfound;
- i := i + 1;
- dbms_output.put_line('A业务名称' || i || ':' || v_businessname);
- par_businessname := v_businessname;
- end loop;
- close cursor_a;
- elsif v_busstype = '2' then
- open cursor_b(v_bussid);
- loop
- fetch cursor_b
- into v_businessname;
- exit when cursor_b%notfound;
- i := i + 1;
- dbms_output.put_line('B业务名称' || i || ':' || v_businessname);
- par_businessname := v_businessname;
- end loop;
- close cursor_b;
- end if;
- end loop;
- end pro_test;
该操作使用到了3个游标,后面两个游标的条件是第一个游标查询的结果,而第一个游标的条件又是存储过程的输入参数。
把待办id=2传入存储过程,得到如下结果:
2.需求二,根据业务类型和业务名称统计该业务的待办数量?
这个需求的关键是在于一个业务名称,因为业务名称是来自表A或表B。首先可以根据业务类型和业务id分组统计出所有的业务id和业务类型,用一个游标来存储:
cursor cursor_flowwork is
select t.business_id as bid, t.business_type as bt
from tbl_flow_work t
group by t.business_id, t.business_type;
再用两个游标分别存储业务表A和业务表B的数据,条件问是第一个游标查询出的业务id:
cursor cursor_a(temp_bussid number) is
select tba.business_name as bn
from tbl_business_a tba
where tba.business_id = temp_bussid;--条件是游标参数
cursor cursor_b(temp_bussid number) is
select tbb.business_name as bn
from tbl_business_b tbb
where tbb.business_id = temp_bussid;--条件是游标参数
最后,通过遍历第一个游标,判断是香港业务还是大陆业务调用对应的游标,得到业务名称,并将业务名称插入临时表。再将临时表的数据存储到输出游标参数。
open par_businessname for
select t.business_type, t.business_name, count(t.business_name)
from tbl_temp_report t
group by t.business_type, t.business_name;
具体sql如下:
- create or replace procedure pro_test1(par_businessname out sys_refcursor) as
- v_bussid number(20);
- v_businessname varchar2(200);
-
-
- cursor cursor_flowwork is
- select t.business_id as bid, t.business_type as bt
- from tbl_flow_work t
- group by t.business_id, t.business_type;
-
- cursor cursor_a(temp_bussid number) is
- select tba.business_name as bn
- from tbl_business_a tba
- where tba.business_id = temp_bussid;
- cursor cursor_b(temp_bussid number) is
- select tbb.business_name as bn
- from tbl_business_b tbb
- where tbb.business_id = temp_bussid;
- begin
-
- delete from tbl_temp_report;
- for cfw in cursor_flowwork loop
- v_bussid := cfw.bid;
- if cfw.bt = '1' then
- open cursor_a(v_bussid);
- loop
- fetch cursor_a
- into v_businessname;
- exit when cursor_a%notfound;
-
- insert into tbl_temp_report
- (business_type, business_name)
- values
- ('香港业务', v_businessname);
- end loop;
- close cursor_a;
- elsif cfw.bt = '2' then
- open cursor_b(v_bussid);
- loop
- fetch cursor_b
- into v_businessname;
- exit when cursor_b%notfound;
-
- insert into tbl_temp_report
- (business_type, business_name)
- values
- ('大陆业务', v_businessname);
- end loop;
- close cursor_b;
- end if;
- end loop;
- commit;
-
- open par_businessname for
- select t.business_type, t.business_name, count(t.business_name)
- from tbl_temp_report t
- group by t.business_type, t.business_name;
- end pro_test1;
统计结果:
- 数量 业务类型 业务名称
- 1 大陆业务 IDIS档案资料
- 1 大陆业务 销售代表新加入
- 1 大陆业务 安利优惠顾客网上补交身份证
- 1 香港业务 安利销售代表申请书
- 2 香港业务 安利优惠顾客申请书
- 1 香港业务 经销商营业执照变更说明书
辅助代码:
- package com.procedure.util;
-
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
-
-
-
-
-
-
-
-
-
-
- public class ConnectionUtils {
- public static final String DRIVER = "oracle.jdbc.OracleDriver";
- public static final String URL = "jdbc:oracle:thin:@192.168.0.153:1521:testdb";
- public static final String USERNAME = "test";
- public static final String USERPASS = "sa";
-
-
- protected Connection getConn() {
- Connection conn = null;
- try {
-
- Class.forName(DRIVER);
- conn = DriverManager.getConnection(URL, USERNAME, USERPASS);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return conn;
- }
-
-
- protected void closeAll(PreparedStatement pstmt, Connection con, ResultSet rs) {
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- if (pstmt != null) {
- try {
- pstmt.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- if (con != null) {
- try {
- con.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
-
- package com.procedure.test;
-
- import java.sql.CallableStatement;
- import java.sql.Connection;
- import java.sql.ResultSet;
- import com.procedure.util.ConnectionUtils;
-
-
-
-
-
-
-
-
-
- public class ProcedureTest extends ConnectionUtils{
- public static void main(String[] args){
- ProcedureTest pt=new ProcedureTest();
- pt.test(2);
- pt.test1();
- }
-
-
-
-
-
- public void test(long fid){
- Connection conn=null;
- CallableStatement cstmt = null;
- try{
- conn=super.getConn();
- cstmt = conn.prepareCall("CALL pro_test (?,?)");
- cstmt.setLong(1,fid);
- cstmt.registerOutParameter(2,java.sql.Types.VARCHAR);
- cstmt.execute();
- System.out.println(cstmt.getString(2));
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- super.closeAll(cstmt, conn, null);
- }
- }
-
-
-
-
-
- public void test1(){
- Connection conn=null;
- CallableStatement cstmt = null;
- try{
- conn=super.getConn();
- cstmt = conn.prepareCall("CALL pro_test1 (?)");
- cstmt.registerOutParameter(1,oracle.jdbc.OracleTypes.CURSOR);
- cstmt.execute();
- ResultSet rs=(ResultSet)cstmt.getObject(1);
- if(rs!=null){
- System.out.println("数量 业务类型 业务名称 ");
- while(rs.next()){
- System.out.println(rs.getString(3)+" "+rs.getString(1)+" "+rs.getString(2));
- }
- }
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- super.closeAll(cstmt, conn, null);
- }
- }
- }
-
- create table TBL_BUSINESS_A
- (
- BUSINESS_ID NUMBER not null,
- BUSINESS_NAME VARCHAR2(60),
- CREATEDATE DATE
- )
- tablespace USERS
- pctfree 10
- initrans 1
- maxtrans 255
- storage
- (
- initial 64K
- minextents 1
- maxextents unlimited
- );
-
- comment on table TBL_BUSINESS_A
- is '香港业务表';
-
- comment on column TBL_BUSINESS_A.BUSINESS_ID
- is '主键,业务id';
- comment on column TBL_BUSINESS_A.BUSINESS_NAME
- is '业务名称';
- comment on column TBL_BUSINESS_A.CREATEDATE
- is '创建时间';
-
- alter table TBL_BUSINESS_A
- add constraint PK_BUSINESS_A_ID primary key (BUSINESS_ID)
- using index
- tablespace USERS
- pctfree 10
- initrans 2
- maxtrans 255
- storage
- (
- initial 64K
- minextents 1
- maxextents unlimited
- );
-
-
-
- create table TBL_BUSINESS_B
- (
- BUSINESS_ID NUMBER not null,
- BUSINESS_NAME VARCHAR2(50),
- CREATEDATE DATE
- )
- tablespace USERS
- pctfree 10
- initrans 1
- maxtrans 255
- storage
- (
- initial 64K
- minextents 1
- maxextents unlimited
- );
-
- comment on table TBL_BUSINESS_B
- is '大陆业务表';
-
- comment on column TBL_BUSINESS_B.BUSINESS_ID
- is '主键,业务id';
- comment on column TBL_BUSINESS_B.BUSINESS_NAME
- is '业务名称';
- comment on column TBL_BUSINESS_B.CREATEDATE
- is '创建时间';
-
- alter table TBL_BUSINESS_B
- add constraint PK_BUSINESS_B_ID primary key (BUSINESS_ID)
- using index
- tablespace USERS
- pctfree 10
- initrans 2
- maxtrans 255
- storage
- (
- initial 64K
- minextents 1
- maxextents unlimited
- );
-
-
-
- create table TBL_FLOW_WORK
- (
- FLOW_WORK_ID NUMBER(20) not null,
- BUSINESS_ID NUMBER(20),
- BUSINESS_TYPE VARCHAR2(1),
- OPERATE_NODE VARCHAR2(20),
- STATUS VARCHAR2(1),
- CREATE_DATE DATE
- )
- tablespace USERS
- pctfree 10
- initrans 1
- maxtrans 255
- storage
- (
- initial 64K
- minextents 1
- maxextents unlimited
- );
-
- comment on table TBL_FLOW_WORK
- is '待办业务表';
-
- comment on column TBL_FLOW_WORK.FLOW_WORK_ID
- is '主键';
- comment on column TBL_FLOW_WORK.BUSINESS_ID
- is '外键,业务表A或B的BUSINESS_ID';
- comment on column TBL_FLOW_WORK.BUSINESS_TYPE
- is '业务类型,1为TBL_BUSINESS_A,2为TBL_BUSINESS_B';
- comment on column TBL_FLOW_WORK.OPERATE_NODE
- is '待办类型,1/资料审核,2/数据录入,3/错误处理,4/电子剪刀';
- comment on column TBL_FLOW_WORK.STATUS
- is '状态
- ,0已结束,1处理中';
- comment on column TBL_FLOW_WORK.CREATE_DATE
- is '待办生成时间';
-
- alter table TBL_FLOW_WORK
- add constraint PK_FLOW_WORK_ID primary key (FLOW_WORK_ID)
- using index
- tablespace USERS
- pctfree 10
- initrans 2
- maxtrans 255
- storage
- (
- initial 64K
- minextents 1
- maxextents unlimited
- );
-
-
-
- create table TBL_TEMP_REPORT
- (
- BUSINESS_TYPE VARCHAR2(10),
- BUSINESS_NAME VARCHAR2(50)
- )
- tablespace USERS
- pctfree 10
- initrans 1
- maxtrans 255
- storage
- (
- initial 64K
- minextents 1
- maxextents unlimited
- );
-
- comment on table TBL_TEMP_REPORT
- is '报表临时表';
-
- comment on column TBL_TEMP_REPORT.BUSINESS_TYPE
- is '业务类型';
- comment on column TBL_TEMP_REPORT.BUSINESS_NAME
- is '业务名称';
参考资料:《Oracle PL/SQL完全自学手册》