使用OLD和NEW对象进行触发器内的动态操作

时间:2012-03-26 11:58:34

标签: oracle triggers new-operator

我想知道是否可以在触发器内使用OLD和NEW对象进行动态操作。

我正在寻找的是这样的: -

  1. ABC是我需要编写Trigger的表。
  2. TracK_Table维护需要跟踪(记录)的表的列列表。
  3. f_log是一个将数据中的更改插入到跟踪(日志)表中的函数。

    CREATE OR REPLACE TRIGGER trg_TRACK
    AFTER INSERT OR UPDATE OR DELETE ON ABC
    FOR EACH ROW
    declare
        v_old_val varchar2(1000);
        v_new_val varchar2(1000);
        n_ret int;
        n_id varchar(50);
    
        cursor cur_col is 
        SELECT  COLUMN_NAME,
            TABLE_name      
        FROM    track_TABLE
        WHERE   upper(TABLE_NAME) = upper('ABC') 
          AND exists (select  cname 
                        from col
                       where UPPER(tname) =upper('ABC') 
                         and upper(cname)=upper(COLUMN_NAME))
          AND upper(allow) = 'Y';
    
    begin
       n_id:= :old.id;
    
       for i_get_col in c_get_col
       loop
          execute immediate 
             'begin 
                :v_old_val:= select '||i_get_col.column_name ||' 
                               from '||:old ||' 
                              where id = '||n_id ||'; 
              end;' using out v_old_val;
          execute immediate 
               'begin 
                 :v_new_val:= select '||i_get_col.column_name ||' 
                                from '||:new ||' 
                                where id = '||n_id ||'; 
                end;' using out v_new_val;
          n_ret := f_log(n_id,i_get_col.column_name,v_old_val,v_new_val);
       end loop;
    end;
    /
    

2 个答案:

答案 0 :(得分:1)

不,您无法动态访问OLD和NEW伪变量。您可以做的是在脚本或过程中使用track_table数据来生成如下所示的静态触发器:

CREATE OR REPLACE TRIGGER trg_TRACK
AFTER INSERT OR UPDATE OR DELETE ON ABC
FOR EACH ROW
DECLARE
  n_id varchar(50);
BEGIN
  n_id := NVL(:old.id, :new.id);
  f_log(:old.id,'COL1',:old.col1,:new.col1);
  f_log(:old.id,'COL3',:old.col3,:new.col3);
  ...
END;

因此,如果TRACK_CHANGES表中的数据发生变化,您只需重新生成触发器。

答案 1 :(得分:1)

一个选项:推送逻辑以检查列是否正在进入f_log过程,然后传递所有列。

例如,如果您的track_Table为您要跟踪的每列保留(table_name,column_name,allow)值,那么

 CREATE OF REPLACE PROCEDURE f_log(  p_id          varchar2  
                                    ,p_table_name  varchar2
                                    ,p_column_name varchar2
                                    ,p_old_val     varchar2
                                    ,p_new_val     varchar2)
 as
    l_exists number;
    cursor chk_column_track IS
        SELECT  1
        FROM    track_TABLE           
        WHERE   upper(TABLE_NAME)  = upper(p_table_name)              
        AND     UPPER(column_name) = upper(p_column_name)
        AND     upper(allow) = 'Y'; 
 begin
    open chk_column_track;
    fetch chk_column_track into l_exists;
    if chk_column_track%found then
       --do the insert here
    end if;
    close chk_column_track;
 end;
 /

 CREATE OR REPLACE TRIGGER trg_TRACK 
 AFTER INSERT OR UPDATE OR DELETE ON ABC 
 FOR EACH ROW 
 DECLARE   
      n_id varchar(50); 
 BEGIN   
    n_id := NVL(:old.id, :new.id);   
    -- send all of the values to f_log and have it decide whether to save them
    f_log(:old.id,'COL1',:old.col1,:new.col1);   
    f_log(:old.id,'COL2',:old.col2,:new.col2);   
    f_log(:old.id,'COL3',:old.col3,:new.col3);   
    ... 
 END; 

为了善良,请大写插入track_table中的值,以便您不必使用UPPER()存储的值,从而使这些值上的任何索引无效!

现在,这将咀嚼一些资源,检查每个操作的每个列名称,但如果您没有运行高容量,那么它可能是可管理的。

否则您将需要更优雅的解决方案。就像利用集合的强大功能和TABLE()子句一样,在批量操作中执行track_table查找。请记住,我现在远离我的数据库,因此我没有对此代码进行测试编译。

    CREATE OR REPLACE TYPE t_audit_row AS OBJECT (
   p_table_name   varchar2(30)
  ,p_column_name  varchar2(30)
  ,p_id           varchar2(50)
  ,p_old_val      varchar2(2000)
  ,p_new_val      varchar2(2000)
);

CREATE OR REPLACE TYPE t_audit_row_table AS TABLE OF t_audit_row;

CREATE OR REPLACE PROCEDURE f_log (p_audit_row_table t_audit_Row_table)
AS
begin
   -- see how we can match the contents of the collection to the values
   -- in the table all in one query. the insert is just my way of showing
   -- how this can be done in one bulk operation. Alternately you could make
   -- the select a cursor and loop through the rows to process them individually.
   insert into my_audit_log (table_name, column_name, id, old_val, new_val)
   select  p_table_name
          ,p_column_name
          ,p_id
          ,p_old_val
          ,p_new_val
   FROM   track_TABLE TT
         ,table(p_audit_row_table) art
   WHERE  tt.TABLE_NAME   = art.p_table_name                       
   AND    tt.column_name  = art.p_column_name         
   AND    tt.allow        = 'Y';
end;
/

CREATE OR REPLACE TRIGGER trg_TRACK   
AFTER INSERT OR UPDATE OR DELETE ON ABC   
FOR EACH ROW   
DECLARE          
  l_id           varchar(50);   
  l_audit_table  t_audit_row_table;
BEGIN        
  l_id := NVL(:old.id, :new.id);        
  -- send all of the values to f_log and have it decide whether to save them   
  l_audit_table := t_audit_row_table (
                      t_audit_row ('ABC','COL1',l_id, :old.col1, :new.col1)  
                      ,t_audit_row ('ABC','COL2',l_id, :old.col2, :new.col2)  
                      ,t_audit_row ('ABC','COL3',l_id, :old.col3, :new.col3)  
                      ,...  
                      ,t_audit_row ('ABC','COLn',l_id, :old.coln, :new.coln)  
                   );
  f_log(l_audit_table);
end;
/