当列在另一个触发器中更改时,ORACLE“更新前”触发器不会触发

vir*_*bee 5 oracle plsql database-trigger

我正在使用 ORACLE 12c。

在一张桌子上,我有 2 个触发器,都是“更新前”。更新列时触发其中一个触发器,并且在此触发器中另一列获得新值。第二个触发器应该在更新第二列时触发。但他没有。

create table TRIGGER_TEST
(
    col1 varchar2(64),
    col2 varchar2(64),
    col3 varchar2(64)
);

create or replace trigger TR_TRIGGER_TEST_1 
before update of COL1 on TRIGGER_TEST
for each row
begin
    dbms_output.put_line('here we are in TR_TRIGGER_TEST_1');
    :new.col2 := 'only testing';
end;
/

create or replace trigger TR_TRIGGER_TEST_2
before update of COL2 on TRIGGER_TEST
for each row
begin
    dbms_output.put_line('here we are in TR_TRIGGER_TEST_2');
    :new.col3 := 'trigger_test_2 has fired';
end;
/


insert into TRIGGER_TEST values ('1_col1','1_col2','1_col3');
select * from TRIGGER_TEST;

COL1                 COL2              COL3                                                    
----------------------------------------------------------------
1_col1               1_col2            1_col3                                                          
Run Code Online (Sandbox Code Playgroud)

插入行后,我执行更新。我希望 COL1="now we will see", COL2="only testing" and COL3 = "trigger_test_2 has fire"。

update TRIGGER_TEST set COL1 = 'now we will see';
Run Code Online (Sandbox Code Playgroud)

但我得到的是:

select * from TRIGGER_TEST;


COL1                 COL2              COL3                                                    
----------------------------------------------------------------
now we will see      only testing      1_col3                                                          
Run Code Online (Sandbox Code Playgroud)

有人可以向我解释一下吗?我真的很确定,对于以前的 ORACLE 版本,这个 szenario 已经奏效了。但现在没有了。

Ale*_*ole 4

我非常确定,在以前的 ORACLE 版本中,这个问题是有效的。

但事实并非如此。我在 11gR2 中运行你的代码并得到相同的结果:

set serveroutput on

update TRIGGER_TEST set COL1 = 'now we will see';

here we are in TR_TRIGGER_TEST_1


1 row updated.

select * from TRIGGER_TEST;

COL1                           COL2                           COL3                          
------------------------------ ------------------------------ ------------------------------
now we will see                only testing                   1_col3                        
Run Code Online (Sandbox Code Playgroud)

before update of COL2 on TRIGGER_TEST是一个DML 事件子句。您正在创建简单的 DML 触发器

DML 触发器在表或视图上创建,其触发事件由 DML 语句 DELETE、INSERT 和 UPDATE 组成。...

当您发出更新时,DML 会导致第一个触发器触发。但是当您在该触发器内分配新值时:

    :new.col2 := 'only testing';
Run Code Online (Sandbox Code Playgroud)

..这不是DML语句 - 它不是单独的更新。

如果以这种方式分配值确实导致触发器触发,那么如果您这样做:

    :new.col1 := 'something';
Run Code Online (Sandbox Code Playgroud)

...然后第一个触发器将再次递归地触发,直到遇到错误ORA-00036: maximum number of recursive SQL levels (50) exceeded。那显然会很糟糕。

col3如果您需要这样做,则必须在第一个触发器中重复分配。对于无论您点击任一触发器都希望发生的更复杂的副作用,您可以有一个过程来执行任何必要的操作(不影响此表),然后从两个触发器调用该操作。不过,您需要一种机制来确保在 DML 更新触及两列时该过程不会被调用两次 - 这导致两个触发器都触发。