2017-02-21 72 views
0

我有一个表递归触发处理

DEPT  ID  ICCODE  OTHER FIELDS 
    ====  ===  =====  ============ 
    10   2  FA   Data1 
    20   2  FA   Data2 
    30   2  FA   Data3 

每个部门下面的数据属于一些外部应用程序。如果任何外部应用程序更改任何部门的ICCODE,我应该使用相同的值更新其他两个部门的ICCODE。

我在列ICCODE上写入触发器并更新其他两个记录,但这里问题是触发器在同一列上,并且在触发器中为不同的行再次修改相同的列值。这是造成死锁的情况。任何人都可以让我知道这个或任何解决方法的解决方案?我无法更改上表的结构,但可以根据需要创建新结构。这里的问题是外部应用程序只更新此表...

问候

+0

你当前的触发器是什么样的?你真的遇到了一个死锁,或者一个变异的表错误,这是非常不同的? –

+0

陷入僵局。最初得到了突变表错误,然后我使用了pragma autonomous_transaction;修复它并得到死锁错误。 – Bujji

回答

1

您的数据模型是一个问题,因为它应该按照Rene的建议进行标准化。但是,考虑到你不能那样做,并且因为你的问题的一部分已经是一个变异的表错误(来自评论)。 假设您使用的是11g或更高版本,则可以使用复合触发器解决这两个问题。

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

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

让我们先从一个虚表和数据第一:

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。使用复合触发器来操纵集合,您可以从语句后触发器更新,但这会被递归调用。

所以这里采用dbms_application_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。

修复数据模型仍然会好得多,但这种方法可能会作为解决您的限制的解决方法。

+0

非常感谢这个解决方案。我不知道复合触发器。在获得解决方案之前,我所做的是1)在表上创建触发器并将数据插入临时表2)创建一个调度程序,用于侦听此临时表上的每秒,并查看是否有任何新记录3)如果有新记录它使用值更新主表(即使在这种情况下,它会再次引发一次触发,但是我有条件放入查询来检查虚拟表,以查看它是否已处理记录,如果它不是,否则会被忽略。 – Bujji

+0

这真的看起来很棒解决方案:) – Bujji

0

您应该创建一个表,一个ID列和CODE列和你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。

+0

很抱歉,我无法控制现有的桌子。我可以创建新表,但不能对现有表进行更改。 – Bujji