ORACLE之游标实战

这是一个忙碌的季节,也是一个收获颇多的季节。在忙碌工作的同时,挤出些时间写一些小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。具体结 构和测试数据如图:

ORACLE之游标实战

 

为了方便浏览,以下才测数据,临时表的数据是动态生成的。

Text代码 
  1. tbl_business_a测试数据:  
  2. BUSINESS_ID BUSINESS_NAME   CREATEDATE  
  3. 1        安利销售代表申请书           2012-12-5 12:00:00  
  4. 2        经销商营业执照变更说明书      2012-12-5 13:01:00  
  5. 3        安利优惠顾客申请书           2012-12-5 12:00:00  
  6. 4        安利优惠顾客申请书           2012-12-5 12:00:00  
  7.   
  8.   
  9. tbl_business_b测试数据:  
  10. BUSINESS_ID BUSINESS_NAME   CREATEDATE  
  11. 1        销售代表新加入                  2012-12-5 12:00:00  
  12. 2        安利优惠顾客网上补交身份证    2012-12-5 12:00:00  
  13. 3        IDIS档案资料                         2012-12-5 12:00:00  
  14.   
  15. tbl_flow_work测试数据:  
  16. FLOW_WORK_ID    BUSINESS_ID BUSINESS_TYPE   OPERATE_NODE    STATUS  CREATE_DATE  
  17. 1        1       1       1       1     2012-12-5 12:00:00  
  18. 2        2       1       2       1     2012-12-5 12:00:00  
  19. 3        3       1       3       1     2012-12-5 12:00:00  
  20. 4        3       1       4       1     2012-12-5 12:00:00  
  21. 5        1       2       1       1     2012-12-5 12:00:00  
  22. 6        2       2       2       1     2012-12-5 12:00:00  
  23. 7        3       2       3       1     2012-12-5 12:00:00  
  24. 8        3       2       4       1     2012-12-5 12:00:00  
  25. 9        3       1       1       0     2012-12-5 12:00:00  
  26. 10       3       2       1       0     2012-12-5 12:00:00  
  27. 11       4       1       1       1     2012-12-5 12:00:00  
  28.   
  29. tbl_temp_report测试数据:  
  30. BUSINESS_TYPE   BUSINESS_NAME  
  31. 香港业务    安利销售代表申请书  
  32. 大陆业务    销售代表新加入  
  33. 香港业务    经销商营业执照变更说明书  
  34. 大陆业务    安利优惠顾客网上补交身份证  
  35. 香港业务    安利优惠顾客申请书  
  36. 大陆业务    IDIS档案资料  
  37. 香港业务    安利优惠顾客申请书  

    

 

1.需求一,获取某一条待办对应的业务类型的业务名称?

现在既然是在学习游标,那么就应该用游标知识来解决该问题。其实很简单,可以声明一个带输入参数和一个输出

参数的存储过程,输入参数我们可以假设为待办表的id,而输出参数当然就是业务名称了。接着根据传入的待办id查询出

业务id和业务类型,即business_id和business_type,并且声明一个游标来存储。然后继续声明两个查询两个主业务表的带参数的游标,参数是业务id,条件也就是根据传入的参数查询,查询字段为业务名称business_name。最后就是遍历游标获得结果了,遍历待办表的游标,从游标中取得业务类型和业务id,代入主业务表的游标,得到业务名称,将业务名称写入输出参数返回。存储过程语句如下:

 

Sql代码 
  1. create or replace procedure pro_test(par_flowworkid   in number,  
  2.                                      par_businessname out varchar2) as  
  3.   v_busstype     varchar2(1); --业务类型  
  4.   v_bussid       number(20); --业务id  
  5.   v_businessname varchar2(200); --业务名称  
  6.   i              number(20);  
  7.   
  8.   --根据待办id获取待办信息  
  9.   cursor cursor_flowwork is  
  10.     select t.business_id as bid, t.business_type as bt  
  11.       from tbl_flow_work t  
  12.      where t.flow_work_id = par_flowworkid;  
  13.   --根据i业务id获取香港信息  
  14.   cursor cursor_a(temp_bussid number) is  
  15.     select tba.business_name as bn  
  16.       from tbl_business_a tba  
  17.      where tba.business_id = temp_bussid;  
  18.   --根据业务id获取大陆信息  
  19.   cursor cursor_b(temp_bussid number) is  
  20.     select tbb.business_name as bn  
  21.       from tbl_business_b tbb  
  22.      where tbb.business_id = temp_bussid;  
  23. begin  
  24.   i := 1; --初始化变量  
  25.   for cfw in cursor_flowwork loop  
  26.     v_busstype := cfw.bt;  
  27.     v_bussid   := cfw.bid;  
  28.     if v_busstype = '1' then  
  29.       open cursor_a(v_bussid);  
  30.       loop  
  31.         fetch cursor_a  
  32.           into v_businessname;  
  33.         exit when cursor_a%notfound;  
  34.         i := i + 1;  
  35.         dbms_output.put_line('A业务名称' || i || ':' || v_businessname);  
  36.         par_businessname := v_businessname; --给输出参数赋值  
  37.       end loop;  
  38.       close cursor_a;  
  39.     elsif v_busstype = '2' then  
  40.       open cursor_b(v_bussid);  
  41.       loop  
  42.         fetch cursor_b  
  43.           into v_businessname;  
  44.         exit when cursor_b%notfound;  
  45.         i := i + 1;  
  46.         dbms_output.put_line('B业务名称' || i || ':' || v_businessname);  
  47.         par_businessname := v_businessname; --给输出参数赋值  
  48.       end loop;  
  49.       close cursor_b;  
  50.     end if;  
  51.   end loop;  
  52. end pro_test;  

该操作使用到了3个游标,后面两个游标的条件是第一个游标查询的结果,而第一个游标的条件又是存储过程的输入参数。

把待办id=2传入存储过程,得到如下结果:

Html代码 
  1. 经销商营业执照变更说明书  

 

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如下:

Sql代码 
  1. create or replace procedure pro_test1(par_businessname out sys_refcursor) as  
  2.   v_bussid       number(20); --业务id  
  3.   v_businessname varchar2(200); --业务名称  
  4.   
  5.   --根据业务类型和业务id分组得到待办信息  
  6.   cursor cursor_flowwork is  
  7.     select t.business_id as bid, t.business_type as bt  
  8.       from tbl_flow_work t  
  9.      group by t.business_id, t.business_type;  
  10.   --根据业务类型和业务id获取所有业务名称  
  11.   cursor cursor_a(temp_bussid number) is  
  12.     select tba.business_name as bn  
  13.       from tbl_business_a tba  
  14.      where tba.business_id = temp_bussid;  
  15.   cursor cursor_b(temp_bussid number) is  
  16.     select tbb.business_name as bn  
  17.       from tbl_business_b tbb  
  18.      where tbb.business_id = temp_bussid;  
  19. begin  
  20.   --先删除临时表的数据,可以做个判断  
  21.   delete from tbl_temp_report;  
  22.   for cfw in cursor_flowwork loop  
  23.     v_bussid := cfw.bid;  
  24.     if cfw.bt = '1' then  
  25.       open cursor_a(v_bussid);  
  26.       loop  
  27.         fetch cursor_a  
  28.           into v_businessname;  
  29.         exit when cursor_a%notfound;  
  30.         --向临时表插入记录  
  31.         insert into tbl_temp_report  
  32.           (business_type, business_name)  
  33.         values  
  34.           ('香港业务', v_businessname);  
  35.       end loop;  
  36.       close cursor_a;  
  37.     elsif cfw.bt = '2' then  
  38.       open cursor_b(v_bussid);  
  39.       loop  
  40.         fetch cursor_b  
  41.           into v_businessname;  
  42.         exit when cursor_b%notfound;  
  43.         --向临时表插入记录  
  44.         insert into tbl_temp_report  
  45.           (business_type, business_name)  
  46.         values  
  47.           ('大陆业务', v_businessname);  
  48.       end loop;  
  49.       close cursor_b;  
  50.     end if;  
  51.   end loop;  
  52.   commit;  
  53.   --统计临时表的数据,  
  54.   open par_businessname for  
  55.     select t.business_type, t.business_name, count(t.business_name)  
  56.       from tbl_temp_report t  
  57.      group by t.business_type, t.business_name;  
  58. end pro_test1;  

统计结果:

Html代码 
  1. 数量    业务类型            业务名称    
  2. 1       大陆业务            IDIS档案资料  
  3. 1       大陆业务            销售代表新加入  
  4. 1       大陆业务            安利优惠顾客网上补交身份证  
  5. 1       香港业务            安利销售代表申请书  
  6. 2       香港业务            安利优惠顾客申请书  
  7. 1       香港业务            经销商营业执照变更说明书  


辅助代码:
Java代码 
  1. package com.procedure.util;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.DriverManager;  
  5. import java.sql.PreparedStatement;  
  6. import java.sql.ResultSet;  
  7. import java.sql.SQLException;  
  8.   
  9.   
  10. /** 
  11.  * @filename ConnectionUtils.java 
  12.  * @description 数据库连接工具 
  13.  * @date 2012-12-5 
  14.  * @time 13:38 
  15.  * @author wst 
  16.  * 
  17.  */  
  18. public class ConnectionUtils {  
  19.     public static final String DRIVER = "oracle.jdbc.OracleDriver";  
  20.     public static final String URL = "jdbc:oracle:thin:@192.168.0.153:1521:testdb";  
  21.     public static final String USERNAME = "test";  
  22.     public static final String USERPASS = "sa";  
  23.   
  24.     //得到连接  
  25.     protected Connection getConn() {  
  26.         Connection conn = null;  
  27.         try {  
  28.             // 装载驱动  
  29.             Class.forName(DRIVER);  
  30.             conn = DriverManager.getConnection(URL, USERNAME, USERPASS);  
  31.         } catch (Exception e) {  
  32.             e.printStackTrace();  
  33.         }  
  34.         return conn;  
  35.     }  
  36.   
  37.     // 关闭资源  
  38.     protected void closeAll(PreparedStatement pstmt, Connection con, ResultSet rs) {  
  39.         if (rs != null) {  
  40.             try {  
  41.                 rs.close();  
  42.             } catch (SQLException e) {  
  43.                 e.printStackTrace();  
  44.             }  
  45.         }  
  46.         if (pstmt != null) {  
  47.             try {  
  48.                 pstmt.close();  
  49.             } catch (SQLException e) {  
  50.                 e.printStackTrace();  
  51.             }  
  52.         }  
  53.         if (con != null) {  
  54.             try {  
  55.                 con.close();  
  56.             } catch (SQLException e) {  
  57.                 e.printStackTrace();  
  58.             }  
  59.         }  
  60.     }  
  61. }  
  62.   
  63.   
  64. package com.procedure.test;  
  65.   
  66. import java.sql.CallableStatement;  
  67. import java.sql.Connection;  
  68. import java.sql.ResultSet;  
  69. import com.procedure.util.ConnectionUtils;  
  70.   
  71. /** 
  72.  * @fileName ProcedureTest.java 
  73.  * @description oracle存储过程测试 
  74.  * @date 2012-12-5 
  75.  * @time 13:54 
  76.  * @author wst 
  77.  * 
  78.  */  
  79. public class ProcedureTest extends ConnectionUtils{  
  80.     public static void main(String[] args){  
  81.         ProcedureTest pt=new ProcedureTest();  
  82.         pt.test(2);  
  83.         pt.test1();  
  84.     }  
  85.       
  86.     /** 
  87.      * 找出某一条待办所对应的业务类型的业务名称 
  88.      * @param fid 待办id 
  89.      */  
  90.     public void test(long fid){  
  91.         Connection conn=null;  
  92.         CallableStatement cstmt = null;  
  93.         try{  
  94.             conn=super.getConn();  
  95.             cstmt = conn.prepareCall("CALL pro_test (?,?)");  
  96.             cstmt.setLong(1,fid);//输入参数  
  97.             cstmt.registerOutParameter(2,java.sql.Types.VARCHAR);//输出参数为字符类型  
  98.             cstmt.execute();  
  99.             System.out.println(cstmt.getString(2));  
  100.         }catch(Exception e){  
  101.             e.printStackTrace();  
  102.         }finally{  
  103.             super.closeAll(cstmt, conn, null);  
  104.         }  
  105.     }  
  106.       
  107.     /** 
  108.      * 
  109.      * 统计某个业务类型的业务名称在待办表中的数量 
  110.      */  
  111.     public void test1(){  
  112.         Connection conn=null;  
  113.         CallableStatement cstmt = null;  
  114.         try{  
  115.             conn=super.getConn();  
  116.             cstmt = conn.prepareCall("CALL pro_test1 (?)");  
  117.             cstmt.registerOutParameter(1,oracle.jdbc.OracleTypes.CURSOR);//输出参数为游标类型  
  118.             cstmt.execute();  
  119.             ResultSet rs=(ResultSet)cstmt.getObject(1);  
  120.             if(rs!=null){  
  121.                 System.out.println("数量      业务类型            业务名称    ");  
  122.                 while(rs.next()){  
  123.                     System.out.println(rs.getString(3)+"        "+rs.getString(1)+"         "+rs.getString(2));  
  124.                 }  
  125.             }  
  126.         }catch(Exception e){  
  127.             e.printStackTrace();  
  128.         }finally{  
  129.             super.closeAll(cstmt, conn, null);  
  130.         }  
  131.     }  
  132. }  
 
Sql代码 
  1. -- Create table  
  2. create table TBL_BUSINESS_A  
  3. (  
  4.   BUSINESS_ID   NUMBER not null,  
  5.   BUSINESS_NAME VARCHAR2(60),  
  6.   CREATEDATE    DATE  
  7. )  
  8. tablespace USERS  
  9.   pctfree 10  
  10.   initrans 1  
  11.   maxtrans 255  
  12.   storage  
  13.   (  
  14.     initial 64K  
  15.     minextents 1  
  16.     maxextents unlimited  
  17.   );  
  18. -- Add comments to the table   
  19. comment on table TBL_BUSINESS_A  
  20.   is '香港业务表';  
  21. -- Add comments to the columns   
  22. comment on column TBL_BUSINESS_A.BUSINESS_ID  
  23.   is '主键,业务id';  
  24. comment on column TBL_BUSINESS_A.BUSINESS_NAME  
  25.   is '业务名称';  
  26. comment on column TBL_BUSINESS_A.CREATEDATE  
  27.   is '创建时间';  
  28. -- Create/Recreate primary, unique and foreign key constraints   
  29. alter table TBL_BUSINESS_A  
  30.   add constraint PK_BUSINESS_A_ID primary key (BUSINESS_ID)  
  31.   using index   
  32.   tablespace USERS  
  33.   pctfree 10  
  34.   initrans 2  
  35.   maxtrans 255  
  36.   storage  
  37.   (  
  38.     initial 64K  
  39.     minextents 1  
  40.     maxextents unlimited  
  41.   );  
  42.   
  43.   
  44. -- Create table  
  45. create table TBL_BUSINESS_B  
  46. (  
  47.   BUSINESS_ID   NUMBER not null,  
  48.   BUSINESS_NAME VARCHAR2(50),  
  49.   CREATEDATE    DATE  
  50. )  
  51. tablespace USERS  
  52.   pctfree 10  
  53.   initrans 1  
  54.   maxtrans 255  
  55.   storage  
  56.   (  
  57.     initial 64K  
  58.     minextents 1  
  59.     maxextents unlimited  
  60.   );  
  61. -- Add comments to the table   
  62. comment on table TBL_BUSINESS_B  
  63.   is '大陆业务表';  
  64. -- Add comments to the columns   
  65. comment on column TBL_BUSINESS_B.BUSINESS_ID  
  66.   is '主键,业务id';  
  67. comment on column TBL_BUSINESS_B.BUSINESS_NAME  
  68.   is '业务名称';  
  69. comment on column TBL_BUSINESS_B.CREATEDATE  
  70.   is '创建时间';  
  71. -- Create/Recreate primary, unique and foreign key constraints   
  72. alter table TBL_BUSINESS_B  
  73.   add constraint PK_BUSINESS_B_ID primary key (BUSINESS_ID)  
  74.   using index   
  75.   tablespace USERS  
  76.   pctfree 10  
  77.   initrans 2  
  78.   maxtrans 255  
  79.   storage  
  80.   (  
  81.     initial 64K  
  82.     minextents 1  
  83.     maxextents unlimited  
  84.   );  
  85.   
  86.   
  87. -- Create table  
  88. create table TBL_FLOW_WORK  
  89. (  
  90.   FLOW_WORK_ID  NUMBER(20) not null,  
  91.   BUSINESS_ID   NUMBER(20),  
  92.   BUSINESS_TYPE VARCHAR2(1),  
  93.   OPERATE_NODE  VARCHAR2(20),  
  94.   STATUS        VARCHAR2(1),  
  95.   CREATE_DATE   DATE  
  96. )  
  97. tablespace USERS  
  98.   pctfree 10  
  99.   initrans 1  
  100.   maxtrans 255  
  101.   storage  
  102.   (  
  103.     initial 64K  
  104.     minextents 1  
  105.     maxextents unlimited  
  106.   );  
  107. -- Add comments to the table   
  108. comment on table TBL_FLOW_WORK  
  109.   is '待办业务表';  
  110. -- Add comments to the columns   
  111. comment on column TBL_FLOW_WORK.FLOW_WORK_ID  
  112.   is '主键';  
  113. comment on column TBL_FLOW_WORK.BUSINESS_ID  
  114.   is '外键,业务表A或B的BUSINESS_ID';  
  115. comment on column TBL_FLOW_WORK.BUSINESS_TYPE  
  116.   is '业务类型,1为TBL_BUSINESS_A,2为TBL_BUSINESS_B';  
  117. comment on column TBL_FLOW_WORK.OPERATE_NODE  
  118.   is '待办类型,1/资料审核,2/数据录入,3/错误处理,4/电子剪刀';  
  119. comment on column TBL_FLOW_WORK.STATUS  
  120.   is '状态  
  121. ,0已结束,1处理中';  
  122. comment on column TBL_FLOW_WORK.CREATE_DATE  
  123.   is '待办生成时间';  
  124. -- Create/Recreate primary, unique and foreign key constraints   
  125. alter table TBL_FLOW_WORK  
  126.   add constraint PK_FLOW_WORK_ID primary key (FLOW_WORK_ID)  
  127.   using index   
  128.   tablespace USERS  
  129.   pctfree 10  
  130.   initrans 2  
  131.   maxtrans 255  
  132.   storage  
  133.   (  
  134.     initial 64K  
  135.     minextents 1  
  136.     maxextents unlimited  
  137.   );  
  138.   
  139.   
  140. -- Create table  
  141. create table TBL_TEMP_REPORT  
  142. (  
  143.   BUSINESS_TYPE VARCHAR2(10),  
  144.   BUSINESS_NAME VARCHAR2(50)  
  145. )  
  146. tablespace USERS  
  147.   pctfree 10  
  148.   initrans 1  
  149.   maxtrans 255  
  150.   storage  
  151.   (  
  152.     initial 64K  
  153.     minextents 1  
  154.     maxextents unlimited  
  155.   );  
  156. -- Add comments to the table   
  157. comment on table TBL_TEMP_REPORT  
  158.   is '报表临时表';  
  159. -- Add comments to the columns   
  160. comment on column TBL_TEMP_REPORT.BUSINESS_TYPE  
  161.   is '业务类型';  
  162. comment on column TBL_TEMP_REPORT.BUSINESS_NAME  
  163.   is '业务名称';  

参考资料:《Oracle PL/SQL完全自学手册》

转载请注明来源:http://9080.iteye.com/blog/1742489

你可能感兴趣的:(oracle)