情景及需求:

有A表为事实表,B为历史表

create table A (fact_id int not null primary key,name varchar2(50));

create table B (log_id int not null primary key,name varchar2(50),addtime timestamp);

  需求:建立存储过程prc,外部程序并行周期调用。该过程从A表取得一定量的数据(记录集)备份到B表,并从A中删除该批备份的数据(记录集),同时返回该批数据(记录集)到外部程序供外部程序使用

 

分析:

要实现上述需求,首先考虑该过程用于并行处理,需要对每一个处理进程进行事务隔离——既需要实现读隔离,oracle中可以通过select ...  from A   for update 实现。而每次处理的记录数可以通过订阅存储过程的入参“rowcount int”实现。返回记录集可以通过一个“rs out ref_syscursor”实现。而关键的部分是如果实现从A到B的记录转移?通常我们可以的定义一个事物级的临时表T来实现:A->T,T[+A]->B,从A删除T,返回T的记录集,删除T。虽然oracle的临时表已经做了很多的性能优化且具有自己的特点,但仍然是和磁盘打交道的做法——如果临时表的数据量很大——这是个很不错的选择,但如果处理的中间数据量不是很大,通过内存变量结构来实现岂不是更好吗!为此sqlserver中提供表变量这一机制,而oracle中复合数据类型[或称集合数据类型]“表类型”正是解决这一问题的机制,为了具有标准sql类似的集合操作oracle提供了在pl/sql中使用的bulk collect、for all操作。

 

实现代码:

create or replace procedure prc
(
    rowcount int,
    rs       out sys_refcursor
) 
as
    TYPE t_fact_id is table of int; --定义table类型
    vt_fact_id      t_fact_id; --定义table类型的变量
    v_first_fact_id int;
    v_last_fact_id  int;
begin
    --批量获取A的fact_id字段信息到t_fact_id表类型的变量vt_fact_id中
    select fact_id bulk collect
    into   vt_fact_id
    from   a
    where  rownum <= rowcount
    for    update
    order  by fact_id;

    --批量插入到B
    forall i in vt_fact_id.first .. vt_fact_id.last
        insert into b
            select a.*, sysdate
            from   a
            where  fact_id = vt_fact_id(i);

    --获取插入到表变量的A表的fact_id的最小值、最大值,主要是为了定义输出结果集的条件
    v_first_fact_id := vt_fact_id(vt_fact_id.first);
    v_last_fact_id  := vt_fact_id(vt_fact_id.last);

    --输出结果集
    open rs for
        select *
        from   a
        where  fact_id between v_first_fact_id and v_last_fact_id;

    --批量删除A中的记录
    forall i in vt_fact_id.first .. vt_fact_id.last
        delete from a
        where  fact_id = vt_fact_id(i);

    commit;

exception
    when others then
        dbms_output.put_line(sqlerrm);
        open rs for
            select *
            from   a
            where  0 = 1;
end;

讨论:

1.如果A表的primary key列fact_id是直接插入到B表的,其实可以使用dml(delete)的returning fact_id bulk collect into vt_fact_id来获得记录集的fact_id表类型变量数据,而不用如例子中先select from A for update了,之后从B表获得返回的结果集

2.例子中没有在正常的程序处理部分处理vt_fact_id是empty(不是null),而是通过exception部分处理的