2017-02-21 77 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