防止并行执行程序

时间:2016-07-14 14:30:33

标签: oracle oracle11g locking

我有一个表触发器,当状态从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;

1 个答案:

答案 0 :(得分: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你会发现更多内容。

正如Nicholas Krasnov正确观察到的那样,在这种触发器中,考虑一组行而不是一行,你没有:new:old值。 获得您需求的方法可能如下,但这是一个棘手的解决方案,我会在生产环境中使用之前仔细检查。

你可以创建一个信号量表来知道你是否必须触发触发器,然后使用两个触发器,一个在行级别,一个在更新之前,一个在表级别,AFTER更新;行级别1检查值并更新信号量表,而更新后触发的表级别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 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>