复合触发器在第一次插入时和不在第一次插入时表现不同

时间:2016-10-29 18:32:11

标签: oracle plsql triggers

我有下面的可运行代码,它只是打印消息并且让我很感兴趣。我没有任何与此表相关的其他触发器(我使用drop table来强化它),但是当第一次触发复合触发器(第一次插入表)时,会发生以下行为:

  • 测试1)BEFORE STATEMENT第一次插入时运行两次,但在此之后只运行一次
  

在声明之前是数量:0在声明之前是数量:0

  • 测试2)变量qty在第一次插入时具有NULL值,在此之后具有预期值。
  

报表后数量:

你能解释一下这种行为吗?

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 个答案:

答案 0 :(得分: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发布的链接。