【问题标题】:Compound trigger behaves different when it's the first insertion and when it's not复合触发器在第一次插入和不是第一次插入时表现不同
【发布时间】:2017-03-12 08:43:19
【问题描述】:

我有下面的可运行代码,它只是打印消息并且很吸引我。我没有与此表相关的任何其他触发器(我做了一个删除表来加强它),但是当第一次触发复合触发器时(第一次在表上插入),会发生以下行为:

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

之前声明数量: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);
/

【问题讨论】:

  • 因为 Oracle 保证触发器最后一次被触发,但不保证它在多用户环境中恰好触发一次,看到这个链接:asktom.oracle.com/pls/asktom/…
  • @krokodilko 这是非常好的解释。

标签: oracle plsql triggers


【解决方案1】:

这是我创建表和触发器时在我的数据库上的输出。

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 在您问题下方的评论中发布的链接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 1970-01-01
    • 2020-04-27
    • 2020-11-22
    • 2012-04-15
    相关资源
    最近更新 更多