Oracle 递归触发器处理

Oracle 递归触发器处理,oracle,triggers,Oracle,Triggers,我在表格中有以下数据 DEPT ID ICCODE OTHER FIELDS ==== === ===== ============ 10 2 FA Data1 20 2 FA Data2 30 2 FA Data3 每个部门都属于一些外部应用程序。如果任何外部应用程序更改

我在表格中有以下数据

  DEPT       ID      ICCODE     OTHER FIELDS
  ====       ===     =====      ============
   10         2        FA         Data1
   20         2        FA         Data2
   30         2        FA         Data3
每个部门都属于一些外部应用程序。如果任何外部应用程序更改任何部门的ICCODE,我应使用相同的值更新其他两个部门的ICCODE

我在ICCODE列上编写触发器并更新其他两条记录,但这里的问题是触发器在同一列上,并且在触发器中为不同的行再次修改相同的列值。它正在造成僵局局面。任何人都可以告诉我这个或任何解决办法的解决方案吗?我无法更改上表的结构,但如果需要,可以创建新表。这里的问题是外部应用程序仅更新此表


关于

您应该创建一个包含ID列和代码列的表,并将您的ICCODE存储在该表中

ID ICCODE
== ====
 1 FA
更改表,使其保存ICCODE表的ID,而不是代码本身

  DEPT       ID      IC_ID     OTHER FIELDS
  ====       ===     =====      ============
   10         2        1         Data1
   20         2        1         Data2
   30         2        1         Data3
如果需要,更新ICCODE表中的代码


查询表时查找ICCODE

您的数据模型是一个问题,因为它应该按照Rene的建议进行标准化。然而,考虑到您不能这样做,并且由于您的部分问题已经是一个变异表错误(来自注释)假设你的体重是11克或更高,你可以用复合触发器解决这两个问题

这是避免突变表错误的方法之一,因为它允许您维护在行级别生成的受影响行的列表,然后在稍后的语句级别使用该集合

这个想法只是修改了一点,以跟踪您是否在语句级触发器中第二次命中触发器,您可以使用它来避免递归:

让我们先从一个虚拟表和数据开始:

create table t42 (DEPT number, ID number, ICCODE varchar2(2), OTHER_FIELDS varchar2(10));

insert into t42 (dept, id, iccode, other_fields) values (10, 1, 'FA', 'Data1');
insert into t42 (dept, id, iccode, other_fields) values (20, 2, 'FA', 'Data2');
insert into t42 (dept, id, iccode, other_fields) values (30, 3, 'FA', 'Data3');
insert into t42 (dept, id, iccode, other_fields) values (40, 4, 'XY', 'Data4');
在没有触发器的情况下,更新一行,如:

update t42 set iccode = 'AF' where id = 1;
将仅将该单行的值设置为AF。使用一个操作集合的复合触发器,您可以从after语句触发器更新该集合,但该触发器将被递归调用

因此,它使用
dbms\u application\u info
(或其他一些机制)来查看更新是来自触发器本身,还是来自其他地方:

create or replace trigger test_trigger
for update of iccode on t42
compound trigger

  -- collection to hold old and new values
  type t_changed_row is record (old_value t42.iccode%type, new_value t42.iccode%type);
  type t_changed_rows is table of t_changed_row;
  l_changed_rows t_changed_rows := t_changed_rows();

  l_fixed_info constant varchar2(30) := 'compound trigger hack';

  after each row is
    l_info varchar2(30);
  begin
    dbms_application_info.read_client_info(l_info);
    if l_info is null or l_info != l_fixed_info then
      -- not in nested update; store old and new values
      l_changed_rows.extend;
      l_changed_rows(l_changed_rows.count).old_value := :old.iccode;
      l_changed_rows(l_changed_rows.count).new_value := :new.iccode;
    end if;
  end after each row;

  after statement is
    l_old_info varchar2(30);
  begin
    -- could check current value here as well but may not be worth it;
    -- the collection will be empty anyway on second-level hit

    -- store existing value to restore later
    dbms_application_info.read_client_info(l_old_info);

    -- set info to block recursion  
    dbms_application_info.set_client_info(l_fixed_info);

    -- update table based on all old/new value pairs at once
    forall i in 1..l_changed_rows.count
      update t42
      set iccode = l_changed_rows(i).new_value
      where iccode = l_changed_rows(i).old_value;

    -- reset info
    dbms_application_info.set_client_info(l_old_info);
  end after statement;

end test_trigger;
/
这将更新所有匹配值:

update t42 set iccode = 'AF' where id = 1;

1 row updated.

select * from t42;

      DEPT         ID IC OTHER_FIEL
---------- ---------- -- ----------
        10          1 AF Data1     
        20          2 AF Data2     
        30          3 AF Data3     
        40          4 XY Data4     
所有FA值都已更改为AF,尽管只有一行显然正在更新


修复数据模型会更好,但考虑到您的限制,这种方法可能是一种解决方法。

很抱歉,我无法控制现有表。我可以创建新表,但不能更改现有表。您当前的触发器是什么样子的?您是否真的遇到了死锁,或者是一个非常不同的变异表错误?遇到了死锁。最初得到了mutating table error,然后我使用pragma autonomy_事务;来修复它,并得到了死锁错误。非常感谢这个解决方案。我不知道复合触发器。在得到解决方案之前,我所做的是1)在表上创建一个触发器并将数据插入临时表2)创建一个调度程序,该程序每秒侦听该临时表,并查看是否有任何新记录3)如果有新记录,它将用值更新主表(即使在这种情况下,它也会再次引发一个触发器,但我在查询中设置了一个条件来检查虚拟表,看它是否已被处理记录。如果不是,它将被处理,否则它将被忽略。