自己写的第一个存储过程,功能却一点也不简单,好有成就感

该功能主要是实现根据传过来的参数,读取接口中间表的数据,解析并新增到MPD主表及其子表中,并将每条数据执行结果,成功或失败反馈回去。若中间表不存在某数据,或发生异常如违反唯一性约束,需将业务表数据操作全部回滚。

折腾了好几天,遇到各种难题,百度了N多资料,终于实现,不容易啊~~ 粗体的地方值得学习。

create or replace procedure INSERT_MPD(crossids in varchar2,
                                       flag     out varchar2,
                                       o_cur    out sys_refcursor) as
begin
  declare
    type t_cursor is ref cursor;
    cur_mpd      t_cursor;
    v_mpdno      ppcp_mpd.mpdno%type;
    v_taref      ppcp_mpd.taref%type;
    v_actype     ppcp_mpd.actype%type;
    v_isrepair   ppcp_mpd.is_repair%type;
    v_ata        ppcp_mpd.ata%type;
    v_effective  ppcp_mpd.effective%type;
    v_effdes     ppcp_mpd.effective_des%type;
    v_rii        ppcp_mpd.rii%type;
    v_tasktype   ppcp_mpd.tasktype%type;
    v_rref       ppcp_mpd.rref%type;
    v_des        ppcp_mpd.description%type;
    v_manhours   ppcp_mpd.manhours%type;
    v_crossid    tdms_ppcp_mpd.crossid%type;
    v_zone       tdms_ppcp_mpd.zone%type;
    v_msthres    tdms_ppcp_mpd.first_interval%type;
    v_msint      tdms_ppcp_mpd.interval%type;
    v_airworth   tdms_ppcp_mpd.airworth%type;
    v_mpdid      ppcp_mpd.mpd_id%type;
    l_sql        varchar2(5000);
    cur_zone     t_cursor;
    cur_airworth t_cursor;
    cur_thres    t_cursor;
    cur_int      t_cursor;
    cur_crossid  t_cursor;
    vv_zone      ppcp_mpd_zone.zone%type;
    vv_airworth  ppcp_mpd_airworth.airworth%type;
    vv_msthres   varchar2(200);
    vv_msint     varchar2(200);
    vv_unit      ppcp_mpd_int.unit%type;
    vv_num       ppcp_mpd_int.ms_threshold%type;
 
    -- 申明数组,保存已处理记录的crossid
    type type_array is table of int index by binary_integer;
    var_array type_array;
    i     int;
 
  begin
 
    i    := 1;
    flag := 'success'; -- 返回值,判断是否执行成功
 
    -- 按逗号分隔字符串的SQL语句
    l_sql := 'WITH A AS 
     (SELECT ''' || crossids || ''' A FROM DUAL)
       select result
          from (SELECT DECODE(B, 0, SUBSTR(A, C), SUBSTR(A, C, B - C)) result
             FROM (SELECT A, B, (LAG(B, 1, 0) OVER(ORDER BY LV)) + 1 C
                  FROM (SELECT A, INSTR(A, '','', 1, LEVEL) B, LEVEL LV
                          FROM A
                        CONNECT BY LEVEL <=
                                   (LENGTH(A) - LENGTH(REPLACE(A, '','', ''''))) + 1)))
      where result is not null';
    open cur_crossid for l_sql;
    loop
      fetch cur_crossid
        into v_crossid;
      exit when cur_crossid%notfound;
   
      l_sql := 'select tpm.actype,
             tpm.is_repair,
             tpm.mpdno,
             tpm.taref,
             tpm.ata,
             tpm.effective,
             tpm.effective_des,
             tpm.rii,
             tpm.tasktype,
             tpm.rref,
             tpm.zone,
             tpm.airworth,
             tpm.first_interval,
             tpm.interval,
             tpm.description,
             tpm.manhours,
             tpm.crossid
        from tdms_ppcp_mpd tpm
       where tpm.crossid = ' || v_crossid;
      open cur_mpd for l_sql;
   
      -- 读取数据 (因为只有一行数据,否则要在外加循环loop) 
      fetch cur_mpd
        into v_actype,
             v_isrepair,
             v_mpdno,
             v_taref,
             v_ata,
             v_effective,
             v_effdes,
             v_rii,
             v_tasktype,
             v_rref,
             v_zone,
             v_airworth,
             v_msthres,
             v_msint,
             v_des,
             v_manhours,
             v_crossid;
   
     
      -- 一定要写在fetch后,fetch用于获取游标结果集中的记录
      -- 属性%ROWCOUNT用于返回一个整数值,显示迄今为止从游标结果集中获取记录的总数。
      -- 查询结果记录为零,表示中间表无该数据,主动抛出异常
      if cur_mpd%rowcount = 0 then
        raise NO_DATA_FOUND;
      end if;
   
      select PPCP_MPD_SEQ.NEXTVAL into v_mpdid from dual;
   
      insert into ppcp_mpd
        (actype,
         is_repair,
         mpdno,
         taref,
         ata,
         effective,
         effective_des,
         rii,
         tasktype,
         rref,
         description,
         manhours,
         updatedwhen,
         mpd_id,
         crossid)
      values
        (v_actype,
         v_isrepair,
         v_mpdno,
         v_taref,
         v_ata,
         v_effective,
         v_effdes,
         v_rii,
         v_tasktype,
         v_rref,
         v_des,
         v_manhours,
         sysdate,
         v_mpdid,
         v_crossid);
   
      --解析MS条目zone   
      l_sql := get_spilt_sql(v_zone); -- 获得分割字符串的SQL语句
      open cur_zone for l_sql;
      loop
        fetch cur_zone
          into vv_zone;
        exit when cur_zone%notfound;
        insert into ppcp_mpd_zone (mpd_id, zone) values (v_mpdid, vv_zone);
      end loop;
      close cur_zone;
   
      --解析MS条目airworth 
      l_sql := get_spilt_sql(v_airworth);
      open cur_airworth for l_sql;
      loop
        fetch cur_airworth
          into vv_airworth;
        exit when cur_airworth%notfound;
        insert into ppcp_mpd_airworth
          (mpd_id, airworth)
        values
          (v_mpdid, vv_airworth);
      end loop;
      close cur_airworth;
   
      --解析MS条目interval 
      -- 分割首检的字符串, 并插入间隔表
      --dbms_output.put_line('v_msthres:' || v_msthres  );
      l_sql := get_spilt_sql(v_msthres);
      open cur_thres for l_sql;
      loop
        fetch cur_thres
          into vv_msthres;
        exit when cur_thres%notfound;
     
        -- 解析数字和单位,插入int表       
        vv_unit := upper(substr(vv_msthres, -2));
        vv_num  := to_number(substr(vv_msthres, 1, length(vv_msthres) - 2));
        --dbms_output.put_line('threshold:' || vv_num || '-' || vv_unit );       
        insert into ppcp_mpd_int
          (mpd_id, unit, ms_threshold)
        values
          (v_mpdid, vv_unit, vv_num);
      end loop;
      close cur_thres;
   
      -- 分割重复检的字符串, 并插入或更新到间隔表
      l_sql := get_spilt_sql(v_msint);             -- 函数,组装按空格分隔字符的SQL查询语句
      open cur_int for l_sql;
      loop
        fetch cur_int
          into vv_msint;
        exit when cur_int%notfound;
        -- 解析数字和单位,插入或更新到int表       
        vv_unit := upper(substr(vv_msint, -2));
        vv_num  := to_number(substr(vv_msint, 1, length(vv_msint) - 2));
     
        update ppcp_mpd_int
           set ms_interv = vv_num
         where mpd_id = v_mpdid
           and unit = vv_unit;
        if SQL%NOTFOUND then
       
   insert into ppcp_mpd_int
            (mpd_id, unit, ms_interv)
          values
            (v_mpdid, vv_unit, vv_num);
        end if;
      end loop;
      close cur_int;
   
      var_array(i) := v_crossid;
      i := i + 1;
   
      close cur_mpd;
   
    end loop; -- 循环读取新增数据结束
    close cur_crossid;
 
    -- 循环插入执行成功的crossid
    for j in 1 .. var_array.count loop
      dbms_output.put_line(var_array(j));
      insert into message_content
        (crossid,
         objectid,
         objectNum,
         objectstatus,
         Objectcontent,
         errormemo,
         errorID)
      values
        (var_array(j), null, null, 'success', null, null, null);
    end loop;
 
    open o_cur for
      select * from message_content;
 
  exception
    when DUP_VAL_ON_INDEX then
      rollback; -- 业务数据先全部回滚
   
      -- 插入执行可以成功记录的crossid
      for j in 1 .. var_array.count loop
        dbms_output.put_line(var_array(j));
        insert_Message(var_array(j), 'success', null, null);   -- 另一个存储过程
      end loop;
   
      -- 插入执行发生异常记录的crossid
      insert_Message(v_crossid, 'fail', '违反唯一性约束', '001');
   
      open o_cur for
        select * from message_content;
      flag := 'fail';
   
    when NO_DATA_FOUND then
      rollback; -- 业务数据先全部回滚
   
      -- 插入执行可以成功记录的crossid
      for j in 1 .. var_array.count loop
        dbms_output.put_line(var_array(j));
        insert_Message(var_array(j), 'success', null, null);
      end loop;
   
      -- 插入执行发生异常记录的crossid
      insert_Message(v_crossid, 'fail', '中间表没有此数据', '101');
   
      open o_cur for
        select * from message_content;
      flag := 'fail';
   
    when others then
      rollback; -- 要求全部回滚
   
      -- 插入执行可以成功记录的crossid
      for j in 1 .. var_array.count loop
        dbms_output.put_line(var_array(j));
        insert_Message(var_array(j), 'success', null, null);
      end loop;
   
      -- 插入执行发生异常记录的crossid
      insert_Message(v_crossid, 'fail', '未知原因', '002');
   
      open o_cur for
        select * from message_content;
      flag := 'fail';
   
  end;
end INSERT_MPD;

你可能感兴趣的:(Oracle存储过程)