2016-10-29 1 views
2

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

  • Test 1) перед постановкой работает в два раза для первого введения, но только один раз после того, что

ДО ЗАЯВЛЕНИЕ IS Кол-во: 0 ДО ВЫСТУПЛЕНИЯ IS Кол-во: 0

  • Тест 2) Значение переменной имеет значение NULL для первой вставки и ожидаемое значение после этого.

ПОСЛЕ ВЫСТУПЛЕНИЯ IS Кол-во:

Можете ли вы объяснить такое поведение?


SET SERVEROUTPUT ON;  
--DROP TABLE teste_var_global; 

CREATE TABLE teste_var_global(
    idVal NUMBER 
); 


create or replace TRIGGER compounder 
FOR UPDATE OR INSERT OR DELETE ON teste_var_global 
COMPOUND TRIGGER 

    qty NUMBER; 

    BEFORE STATEMENT IS 
     BEGIN 
     SELECT COUNT(*) INTO qty FROM teste_var_global; 
     DBMS_OUTPUT.PUT_LINE('BEFORE STATEMENT IS'); 
     DBMS_OUTPUT.PUT_LINE('Qty: ' || qty); 
    EXCEPTION 
     WHEN OTHERS THEN 
     DBMS_OUTPUT.PUT_LINE('(' || SQLCODE || ') - ' || SQLERRM); 
    END BEFORE STATEMENT; 

    BEFORE EACH ROW IS 
    BEGIN 
     DBMS_OUTPUT.PUT_LINE(CHR(9)||'BEFORE EACH ROW IS'); 
     IF INSERTING THEN 
      DBMS_OUTPUT.PUT_LINE(CHR(9)||':new.idVal ' || :new.idVal); 
     ELSIF DELETING THEN 
      DBMS_OUTPUT.PUT_LINE(CHR(9)||':old.idVal ' || :old.idVal); 
     ELSE --UPDATING 
      DBMS_OUTPUT.PUT_LINE(CHR(9)||':old.idVal ' || :old.idVal); 
      DBMS_OUTPUT.PUT_LINE(CHR(9)||':new.idVal ' || :new.idVal); 
     END IF; 
    END BEFORE EACH ROW; 

    AFTER EACH ROW IS 
    BEGIN 
     DBMS_OUTPUT.PUT_LINE(CHR(9)||'AFTER EACH ROW IS'); 
     IF INSERTING THEN 
      DBMS_OUTPUT.PUT_LINE(CHR(9)||':new.idVal ' || :new.idVal); 
      DBMS_OUTPUT.NEW_LINE(); 
      qty:= qty + 1; -- increment 
     ELSIF DELETING THEN 
      DBMS_OUTPUT.PUT_LINE(CHR(9)||':old.idVal ' || :old.idVal); 
      DBMS_OUTPUT.NEW_LINE(); 
      qty:= qty -1; -- decrement 
     ELSE --UPDATING 
      DBMS_OUTPUT.PUT_LINE(CHR(9)||':old.idVal ' || :old.idVal); 
      DBMS_OUTPUT.PUT_LINE(CHR(9)||':new.idVal ' || :new.idVal); 
      DBMS_OUTPUT.NEW_LINE(); 
     END IF; 
    END AFTER EACH ROW; 

    AFTER STATEMENT IS 
    BEGIN 
     DBMS_OUTPUT.PUT_LINE('AFTER STATEMENT IS'); 
     DBMS_OUTPUT.PUT_LINE('Qty: ' || qty); 
     DBMS_OUTPUT.NEW_LINE(); 
    END AFTER STATEMENT; 
    END; 
/

-- Test 1, will fire BEFORE STATMENT twice when the first row is inserted 
INSERT INTO teste_var_global 
(
SELECT 1 FROM DUAL 
UNION ALL 
SELECT 2 FROM DUAL 
UNION ALL 
SELECT 3 FROM DUAL 
) 
/

--Test 2, qty will become NULL when this trigger is fired for the first time 
--drop the table and rerun the trigger before executing this command 
INSERT INTO teste_var_global VALUES(1); 
/
+1

Потому что Oracle гарантирует, что триггер будет запущен ** в последний раз **, но не гарантирует, что он запущен ** ровно один раз ** в многопользовательской среде, см. Эту ссылку: https://asktom.oracle .com/pls/asktom/f? p = 100: 11: 0 :::: P11_QUESTION_ID: 2599480800346313755 – krokodilko

+0

@krokodilko Это очень хорошее объяснение. – Kacper

ответ

0

Это, как выглядит вывод на моей БД, когда я создал таблицу и триггер.

BEFORE STATEMENT IS 
Qty: 0 
    BEFORE EACH ROW IS 
    :new.idVal 1 
    AFTER EACH ROW IS 
    :new.idVal 1 

    BEFORE EACH ROW IS 
    :new.idVal 2 
    AFTER EACH ROW IS 
    :new.idVal 2 

    BEFORE EACH ROW IS 
    :new.idVal 3 
    AFTER EACH ROW IS 
    :new.idVal 3 

AFTER STATEMENT IS 
Qty: 3 

Второй тест дает:

BEFORE STATEMENT IS 
Qty: 0 
    BEFORE EACH ROW IS 
    :new.idVal 1 
    AFTER EACH ROW IS 
    :new.idVal 1 

AFTER STATEMENT IS 
Qty: 1 

Но посмотрите, пожалуйста, ссылки, размещенные krokodilko в комментарии ниже Ваш вопрос.