2014-03-19 55 views
0

作为审计/历史记录功能的一部分,我想使用AFTER UPDATE触发器或任何其他触发器来处理以下情形,请让我知道。 方案 -Oracle中AFTER UPDATE触发器中的死锁场景

  1. 将有2个表 - 基表和历史表
  2. 在基表中的第一个插入相同记录的任何记录的更新(更新)历史上的表旧值。
  3. 使用基础表中的新值更新记录。

我正在使用以下触发器,给出死锁情况。请建议解决此问题。

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; 
+0

真的是一个僵局?不是一个突变的表错误,或资源不足(因为触发器会重复从它内部的recursove更新)? –

+0

显示它的死锁错误(ORA-00060)。为避免反复发生火灾情况(如:New.version = Old.Version),则执行INSERT和UPDATE操作。 – SandeepS

+0

但是你还在发射一个额外的更新,这很丑,我很惊讶的作品。你为什么不调整版本(设置':new.version'),并在'before'触发器中创建历史记录,而不是做额外的DML? –

回答

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; 

基表上附加的更新将是冗余和有问题的,因为这将导致循环无限递归。

你也不能在触发器中提交。无论如何你都不想承诺,这打破了交易逻辑。 不提交允许主事务在一个很好的原子块中回滚历史表和主表。