【发布时间】: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 这是非常好的解释。