Oracle中AFTER UPDATE触发器中的死锁方案

时间:2014-03-19 07:31:01

标签: oracle triggers deadlock

作为审计/历史记录功能的一部分,我想使用AFTER UPDATE触发器或任何其他触发器来处理以下场景,请告诉我。     场景 -

  1. 将有2个表 - 基表和历史表
  2. 更新基表中的任何记录时,在历史记录表中使用旧值插入相同记录(待更新)。
  3. 使用基表中的新值更新记录。
  4. 我正在使用以下触发器,给出死锁场景。请建议解决此问题。

    create table Base_table(
    SYMBOL_ID            NUMBER(9)  primary key,  
    SYMBOL_NAME         VARCHAR2(20) ,
    PRICE           NUMBER(9) ,   
    VERSION              NUMBER(1)
    )
    organization index;
    
    create table base_table_hist(
    ID               NUMBER(9) primary key,
    SYMBOL_ID            NUMBER(9) ,   
    SYMBOL_NAME          VARCHAR2(20) ,
    PRICE             NUMBER(9),    
    VERSION              NUMBER(1) ,
    constraint other_symbolid  foreign key(symbol_id) references test_symbol(symbol_id)
    )
    organization index;
    ************************************************************
    
    create or replace Trigger Symbol_Ver
    AFTER UPDATE ON Base_table
    REFERENCING NEW AS New OLD AS Old
    FOR EACH ROW
    
    DECLARE
    new_version number(5);
    --Pragma AUTONOMOUS_TRANSACTION;
    Sid number(9);
    begin
      if (:New.symbol_id <> :Old.symbol_id)    OR (:New.price <> :Old.price)  then
    
    
        new_version:= :Old.version+1;
    
        --insert into history table
        insert into base_table_hist (id, symbol_id, symbol_name,price,version) 
          values (symbol_seq.nextval, :OLD.symbol_id, :OLD.symbol_name, :OLD.price, :OLD.version);
      commit;
      DBMS_OUTPUT.put_line('new_version..'||new_version);
    end if;
    
    if (:New.symbol_id <> :Old.symbol_id)    OR (:New.price <> :Old.price)  then
        update base_table set version=new_version where symbol_id=:Old.symbol_id;
    end if;
    
    end;
    

1 个答案:

答案 0 :(得分:1)

自治事务创建新的独立事务。所以你用两个事务更新同一行,导致死锁。

这里不需要自动触发器。实际上,您不希望在触发器中使用DML触摸基表。这总是有问题的。

幸运的是,在这里您可以使用常规BEFORE触发器(因为您正在更新字段):

CREATE OR REPLACE TRIGGER Symbol_Ver
   BEFORE UPDATE ON Base_table
   FOR EACH ROW
BEGIN
   IF (:New.symbol_id <> :Old.symbol_id) OR (:New.price <> :Old.price) THEN

      -- this will change the value in the row being updated
      :new.version := :Old.version + 1;

      --insert into history table
      INSERT INTO base_table_hist
         (id, symbol_id, symbol_name, price, version)
      VALUES
         (symbol_seq.nextval, :OLD.symbol_id, 
          :OLD.symbol_name, :OLD.price, :OLD.version);
      -- COMMIT <-- don't commit in a trigger!
      DBMS_OUTPUT.put_line('new_version..' || new_version);
   END IF;
END;

基表的额外更新既冗余又有问题,因为这会导致循环无限递归。

此外,您无法提交触发器。无论如何你都不想提交,这会破坏事务逻辑。 不提交允许主事务在一个漂亮的原子块中回滚历史表和主表。