在pl/sql触发器的赋值上卡住了

woobm2wo  于 2021-07-24  发布在  Java
关注(0)|答案(2)|浏览(354)

我必须在插入时为一个表编写一个触发器,该触发器将根据“捐赠者”是否已经做出了“保证”来自动设置表中某一列的值。我试过几种不同的方法,我能做的最好的就是一个触发器导致直接递归,另外三个触发器都导致表错误的变异。我现在所做的一切似乎只会让事情变得更糟,而且找不到解决办法。有人请帮忙。
表格:

CREATE TABLE DD_Pledge (
idPledge number(5),
idDonor number(4),
Pledgedate DATE,
Pledgeamt number(8,2),
idProj number(5),
idStatus number(2),
Writeoff number(8,2),
paymonths number(3),
Campaign number(4),
Firstpledge char(1),
CONSTRAINT pledge_id_pk PRIMARY KEY(idPledge),
CONSTRAINT pledge_idDonor_fk FOREIGN KEY (idDonor) REFERENCES dd_donor (idDonor),
CONSTRAINT pledge_idProj_fk FOREIGN KEY (idProj) REFERENCES dd_project (idProj),
CONSTRAINT pledge_idStatus_fk FOREIGN KEY (idStatus) REFERENCES dd_status (idStatus));

递归触发器:

CREATE OR REPLACE TRIGGER firstpledge_tr
    BEFORE INSERT ON dd_pledge
    FOR EACH ROW
DECLARE
    tr_firstpledge dd_pledge.firstpledge%TYPE;
    iddonor_count INTEGER;
BEGIN  
    SELECT COUNT(iddonor) INTO iddonor_count FROM dd_pledge WHERE iddonor = :NEW.iddonor;
    IF iddonor_count > 0 THEN
        tr_firstpledge := 'N';
    ELSE 
        tr_firstpledge := 'Y';
    END IF;
    INSERT INTO dd_pledge(idpledge,iddonor,pledgedate,pledgeamt,idproj,idstatus,writeoff,paymonths,campaign,firstpledge)
        VALUES (:NEW.idpledge,:NEW.iddonor,:NEW.pledgedate,:NEW.pledgeamt,:NEW.idproj,:NEW.idstatus,:NEW.writeoff,:NEW.paymonths,:NEW.campaign,tr_firstpledge);
   -- COMMIT;
END;

突变触发1:

CREATE OR REPLACE TRIGGER firstpledge_tr
    AFTER INSERT ON dd_pledge
    FOR EACH ROW
DECLARE
    tr_firstpledge dd_pledge.firstpledge%TYPE;
    iddonor_count INTEGER;
BEGIN  
    SELECT COUNT(iddonor) INTO iddonor_count FROM dd_pledge WHERE iddonor = :NEW.iddonor;
    UPDATE dd_pledge
            SET firstpledge = CASE WHEN iddonor_count<1 THEN 'N' ELSE 'Y' END
            WHERE idpledge = :NEW.idpledge;
END;
z8dt9xmd

z8dt9xmd1#

实际的插入操作无论如何都会发生。只需设置新值:

CREATE OR REPLACE TRIGGER firstpledge_tr
    BEFORE INSERT ON dd_pledge
    FOR EACH ROW
DECLARE
    tr_firstpledge dd_pledge.firstpledge%TYPE;
    iddonor_count INTEGER;
BEGIN  
    SELECT COUNT(iddonor) INTO iddonor_count FROM dd_pledge WHERE iddonor = :NEW.iddonor;
    IF iddonor_count > 0 THEN
        tr_firstpledge := 'N';
    ELSE 
        tr_firstpledge := 'Y';
    END IF;
    :NEW.firstpledge := tr_firstpledge;
END;
wko9yo5t

wko9yo5t2#

任何时候,只要你设法让自己得到一个变异表错误,你就有一个设计缺陷(也请参阅ask tom,旧的,但仍然有效)。在这种情况下,您不应该使用触发器,而是在发出insert语句之前,在业务逻辑层中确定并设置tru firstassempt。但是如果你坚持使用触发器,那么你需要一个复合触发器。这避免了错误,但在多用户环境中仍可能带来麻烦。

create or replace trigger first_pledge_ctrig 
    for insert on dd_pledge   
    compound trigger     

    k_is_first_pledge     constant dd_pledge.tr_firstpledge%type := 'Y';
    k_is_first_not_pledge constant dd_pledge.tr_firstpledge%type := 'N';
    type      doners_type is table of dd_pledge.iddonor%type;   
    v_doners  doners_type := doners_type();    

    before each row is    
    begin  
        v_doners.extend;
        v_doners (v_doners.count)  :=  :new.iddonor;    
        :new.tr_firstpledge := k_is_first_not_pledge;     
    end before each row;    

    after statement is                  
    begin      
       forall i_doner in 1 .. v_doners.count
         update dd_pledge   p1 
            set tr_firstpledge = k_is_first_pledge
          where p1.iddonor = v_doners(i_doner) 
            and not exists 
                ( select null 
                    from dd_pledge p2
                   where p1.iddonor = p2.iddonor
                     and p1.rowid != p2.rowid
                );    
    end after statement;    
end first_pledge_ctrig;

这完全避免了在每一行处理过程中实际访问表(这是导致变异表错误的原因)。通常情况下,你可以选择逃脱-但这里的情况下,没有。看这里的小提琴。
它的功能:
声明部分:设置并初始化一个集合,以保存后续的idprovider值。它创建了一对常数。
before行部分:保存上述集合中的当前idconsor。它还设定了“n”的第一个承诺,假设随着时间的推移,你会有更多的重复捐赠者而不是新的捐赠者。
最后,after statement部分:对于已保存但没有事先质押的idprovider值,将首次质押撤销为“y”。

相关问题