日期:2014-05-17  浏览次数:21229 次

一个oracle触发器的优化请教(用友NC产品)
oracle数据库是用在我们公司用友NC财务软件上,NC版本是5.02。
我是公司的NC的系统管理员。
我们使用了资金管理系统,需要使用一个功能是填制结算凭证,如下图:

  上图红色方框标示的是需要特别注意的,系统的要求必须“账户”和“客商辅助”必须一致,也就是这里的“账户”是北京公司,那么后面的“客商辅助”也必须选择北京公司,否则会出现账务和系统上的麻烦。
  备注:我们公司对同一个分公司,账户和客商是同一个编码,比如北京公司,账户和客商编码都是010201,上海公司都是010301.
   
  我经过查看后台数据库,在填制结算凭证的时候,影响到的是两个表,分包是结算分录fts_voucher_b,结算凭证fts_voucher,这两个表通过字段pk_voucher关联。
  上图中的“账户”和“客商辅助”是存在fts_voucher_b中,但是不是直接的代码,比如010201,而是两个pk代码值。
  经过几天的研究,我打算通过触发器完成这个账户和客商一致的问题,可是上面也说到,在填制结算凭证,后台存入fts_voucher_b中的是pk值,必须要凭证保存到数据库之后才能查询到不一致的情况。
  查询结算分录账户和客商不一致的情况,我sql如下:
 
SQL code
select 
       bd_accid.accidcode,      
       gl_freevalue.valuecode    
  from fts_voucher_b, bd_accsubj, gl_freevalue, bd_accid
 where fts_voucher_b.pk_subject = bd_accsubj.pk_accsubj
   and fts_voucher_b.pk_ass = gl_freevalue.freevalueid
   and fts_voucher_b.dr = '0'
   and fts_voucher_b.pk_corp = '1162'
   and fts_voucher_b.pk_account = bd_accid.pk_accid
   and length(gl_freevalue.valuecode) = '6'
   and substr(gl_freevalue.valuecode, 0, 1) = '0'
   and bd_accid.accidcode != gl_freevalue.valuecode  


  这里为了将账户和客商的pk值转化为数字代码(上面备注说到的北京公司010201),通过了4个表的关联查询。
  之后触发器把上面的代码作为了游标,触发器如下:
 
SQL code
create or replace trigger CHECK_VOUCHER

  before insert or update on fts_voucher  

  for each row

declare

  -- local variables here
  pk_bas char(20);

  accode char(6);
  vcode char(6);
 

 

  cursor basjob(id char) is

    select fts_voucher_b.pk_voucher,bd_accid.accidcode, gl_freevalue.valuecode

      from fts_voucher_b,bd_accid, gl_freevalue

     where fts_voucher_b.pk_voucher = id

       and     fts_voucher_b.pk_ass = gl_freevalue.freevalueid

               and fts_voucher_b.pk_account = bd_accid.pk_accid

               and length(gl_freevalue.valuecode) = '6'

               and substr(gl_freevalue.valuecode, 0, 1) = '0';

               

 

begin

  open basjob(:new.pk_voucher);

  loop

 

    fetch basjob

      into pk_bas, accode , vcode;

    exit when basjob%notfound;

    --raise_application_error(-20001,pk_accidcode||'hhh');

  end loop;

  close basjob;

 

  if pk_bas is not null then
    if accode<>vcode then

    begin

      raise_application_error(-20001,'结算凭证号'||:new.cent_typeid||'账户和客商不一致,请修改!');

    end;

  end if;
  end if;

end CHECK_VOUCHER;

  解释:触发器是写在了表结算凭证fts_voucher上而不是写在结算分录fts_voucher_b上,因为上面提到凭证没有保存的时候是不会相应游标里面的sql。 这里借用了fts_voucher和fts_voucher_b共同有一个同样的值pk_voucher。触发器是在审核结算凭证的时候引起触发(审核的时候fts_voucher会有新值传入)
  目前,这个触发器已经完成了如果填制的结算凭证账户和客商不一致引起触发报错不给审核额通过。
  但是很快发现了问题,就是审核发现错误之后,让制单人回到填制结算凭证那里修改账户和客商保证一致,保存的时候同意会报错。
  基本的原因是知道的,在修改结算凭证的时候,pk_voucher并没有变动,错误的结算凭证还在fts_voucher_b里面,系统在保存之前和过程中还是识别的这个没有改的错误的结算凭证,因此即使修改了也是不让保存。
  目前我只要让同事删除这个结算凭证,重新制单。
  现在求助csdn中的大侠。
  有几点疑问:
  1,这个问题还有解决的可能性吗?就是触发器在触发报错之后,制单人可以直接原有基础上修改而不用删除重新制单。
  2,可否使用after触发器,让在保存之后报错,这时候触发器直接写在fts_voucher_b上?
  3,是不是我的触发器写的不够好,还有其他的优化?
  如果方便,可以加我qq344529501,谢谢!
  


------解决方案--------------------
我理解你的说的是,对于原有单据的修改,因触发器判断唯一性而暴错.

建议你在触发器中加一个判断,如果这是一个修改操作,就不判断唯一性,只有插入操作判断.

你代码有下面片断: 
create or replace trigger CHECK_VOUCHER

before insert or update on fts_voucher -- insert和update 分开判断
for each row


------解决方案--------------------
探讨

引用:

把这个问题抛给用友,让他们出解决方案。你私自动是不对的。

报价会好几万的吧,自己解决最好,刚好