Застрял в назначении для триггеров PL / SQL - PullRequest
0 голосов
/ 04 августа 2020

Мне нужно написать триггер для таблицы при вставке, который будет автоматически устанавливать значение для одного из столбцов таблицы в зависимости от того, сделал ли «донор» уже «залог». Я пробовал несколько разных способов, и лучшее, что я мог сделать, это 1 триггер, который привел к прямой рекурсии, и 3 других, которые оба привели к ошибкам мутации таблиц. Я нахожусь в точке, где все, что я делаю, только усугубляет ситуацию, и я не могу найти решения. Кто-нибудь, пожалуйста, помогите.

Таблица:

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;

Ответы [ 2 ]

0 голосов
/ 05 августа 2020

Каждый раз, когда вам удается получить ошибку изменяющейся таблицы, у вас есть недостаток дизайна (см. Также Спросите Тома , более старый, но все еще действующий). В этом случае вы не должны использовать триггер, а должны определять и устанавливать tr_firstpledge на уровне Business Logi c до того, как будет выдан оператор вставки. Однако если вы настаиваете на триггере, вам нужен составной триггер. Это позволяет избежать ошибки, но по-прежнему может вызывать проблемы в многопользовательской среде.

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;

Это полностью позволяет избежать фактического доступа к таблице во время обработки каждой строки (это причина ошибки изменяющейся таблицы). Обычно вы можете обойтись без select, но есть ситуации, когда это не так. Смотрите скрипку здесь . Что он делает:

  • Раздел объявления: Устанавливает и инициализирует коллекцию для хранения последующих значений iddonor. И он создает пару констант.
  • Раздел перед строкой: сохраняет текущий iddonor в вышеупомянутой коллекции. Он также устанавливает для первого залога значение «N», исходя из предположения, что со временем у вас будет больше повторных доноров, чем новых. без предварительного залога.
0 голосов
/ 04 августа 2020

Фактическое действие INSERT все равно произойдет. Вам просто нужно установить НОВОЕ значение:

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;
...