2016-07-14 86 views
0

我有一個表觸發器,它在狀態從2變爲3時調用一個過程。該過程檢查整個數據組(group_id)是否處於狀態3,然後執行一些操作。防止程序並行執行

但現在我面臨的問題是,當我同時設置狀態3的整個數據組時,該過程被多次調用並多次執行此操作。我怎樣才能防止他?例如用鎖

這裏是我的程序查詢:

 SELECT COUNT(*) 
    INTO nResult 
    FROM ticket 
    WHERE group_id = nGroupId 
    AND statusid BETWEEN 0 AND 2; 

    /* If not all tickets of group in status 3, no action required */ 
    IF nResult != 0 THEN 
     RETURN; 
    END IF; 

這是我的觸發器:

IF (:NEW.STATUSID = 3 AND :OLD.STATUSID = 2) THEN 
    myprocedure(:NEW.group_id); 
END IF; 
+0

根據你的解釋程序將被稱爲多次出現了許多變化,從2到3,因爲你已經創建了一個行級觸發器 - 這是預期的行爲。 –

+0

是的,它是預期的行爲,但現在我需要一個預防措施,以避免執行並行並多次爲同一組執行此操作 – ZerOne

回答

1

你可能有一個行級觸發器,即觸發每次行更新;例如:

SQL> create table trigger_table(status number); 

Table created. 

SQL> insert into trigger_table values (1); 

1 row created. 

SQL> insert into trigger_table values (2); 

1 row created. 

SQL> insert into trigger_table values (3); 

1 row created. 

SQL> create trigger update_trigger 
    2 after update on trigger_table 
    3 for each row /* ROW LEVEL */ 
    4 begin 
    5  dbms_output.put_line('change'); 
    6 end; 
    7/

Trigger created. 

SQL> set serveroutput on 
SQL> update trigger_table set status = 1; 
change 
change 
change 

3 rows updated. 

你需要一個表級觸發器,每次更新語句後觸發:

SQL> create or replace trigger update_trigger 
    2 after update on trigger_table 
    3 begin 
    4  dbms_output.put_line('change'); 
    5 end; 
    6/

Trigger created. 

SQL> update trigger_table set status = 1; 
change 

3 rows updated. 

Here你會發現更多的東西。

正如尼古拉斯克拉斯諾夫所觀察到的那樣,在這種觸發器中,考慮一組行而不是一個行,您沒有:new:old值。 獲得您的需求的方法可能如下,但這是一個棘手的解決方案,我在使用生產環境之前仔細檢查它。

你可以創建一個信號量表來知道你是否必須觸發觸發器,然後使用兩個觸發器,一個在行級別,BEFORE更新,另一個在表級別,更新後;行級別檢查值並更新信號量表,而更新後觸發的表級別讀取信號量,如有必要調用您的過程,然後重置信號量。 例如:

SQL> create table trigger_table(status number); 

Table created. 

SQL> insert into trigger_table values (1); 

1 row created. 

SQL> insert into trigger_table values (2); 

1 row created. 

SQL> insert into trigger_table values (3); 

1 row created. 

SQL> create table checkChange (fire varchar2(3)); 

Table created. 

SQL> insert into checkChange values ('NO'); 

1 row created. 

SQL> create or replace trigger before_update_trigger 
    2 before update on trigger_table 
    3 for each row /* ROW LEVEL */ 
    4 begin 
    5  if :new.status = 3 and :old.status = 2 then 
    6   update checkChange set fire = 'YES'; 
    7  end if; 
    8 end; 
    9/

Trigger created. 

SQL> create or replace trigger after_update_trigger 
    2 after update on trigger_table 
    3 declare 
    4 vFire varchar2(3); 
    5 begin 
    6  select fire 
    7  into vFire 
    8  from checkChange; 
    9  if vFire = 'YES' then 
10   dbms_output.put_line('change'); 
11   update checkChange set fire = 'NO'; 
12  end if; 
13 end; 
14/

Trigger created. 

SQL> update trigger_table set status = 2; 

3 rows updated. 

SQL> update trigger_table set status = 3; 
change 

3 rows updated. 

SQL> 
+0

@NicholasKrasnov:好點,編輯答案,使OP考慮這一點。謝謝 – Aleksej

+0

因爲我需要:新的和舊的屬性,這不是我的需要的好解決方案。這種需求是否沒有Oracle實現鎖? – ZerOne

+0

@ZerOne:剛剛編輯顯示您的問題可能的解決方案。 – Aleksej