如何识别模式触发器中的更改值?

时间:2012-03-17 18:23:02

标签: oracle plsql triggers

有人可以告诉我如何在模式触发器中找到新旧值之间的差异吗? 触发器在INSERTUPDATEDELETE上运行。我知道trgigger可以使用:old.*:new.*

2 个答案:

答案 0 :(得分:4)

我认为你的意思是你有一个行级触发器,而不是一个模式级别的触发器。例如,某人在架构中的表上执行DDL时会触发架构级触发器。在谈论模式级别的触发器时,谈论新旧值是没有意义的。

如果我们讨论的是行级触发器,我认为你只对UPDATE语句感兴趣。如果要插入或删除数据,则自然会更改表格中的每一列。

因此,如果我们讨论在UPDATE上触发的行级触发器,则可以使用UPDATING函数。这告诉您是否正在更新特定列。但是,它并不一定告诉您数据正在被更改。声明

UPDATE table_name
   SET col1 = col1;

COL1中的每一行更新TABLE_NAME,但实际上并未更改任何数据。如果这是可以接受的,你可以做类似

的事情
CREATE TRIGGER trg_table_name
  BEFORE UPDATE ON table_name
  FOR EACH ROW
BEGIN
  IF( updating( 'COL1' ) )
  THEN
    <<col1 was updated>>
  END IF;

  IF( updating( 'COL2' ) )
  THEN
    <<col2 was updated>>
  END IF;

  ...
END;

你可以通过循环USER_TAB_COLS中的数据来使动态更加动态,即

SQL> ed
Wrote file afiedt.buf

  1  create table foo (
  2    col1 number,
  3    col2 number,
  4    col3 number
  5* )
SQL> /

Table created.

SQL> create trigger trg_foo
  2    before update on foo
  3    for each row
  4  begin
  5    for cols in (select *
  6                   from user_tab_cols
  7                  where table_name = 'FOO')
  8    loop
  9      if updating( cols.column_name )
 10      then
 11        dbms_output.put_line( 'Updated ' || cols.column_name );
 12      end if;
 13    end loop;
 14  end;
 15  /

Trigger created.

SQL> set serveroutput on;
SQL> insert into foo values( 1, 2, 3 );

1 row created.

SQL> update foo
  2     set col2 = col2 + 1,
  3         col3 = col3 * 2;
Updated COL2
Updated COL3

1 row updated.

虽然这可以显示所有正在更新的列,但主要的缺点是无法以类似的动态方式访问:new:old值。因此,您可以发现COL2已更新,但您无法确定:new.col2:old.col2值的内容,而无需静态引用这些值。

潜在地,根据您尝试解决的问题,您可以编写动态生成触发器的代码,方法是查看数据中的数据。此块的某些内容将生成一个特定表的触发器,该表打印出所有:new

DECLARE
  l_tbl_name VARCHAR2(100) := 'FOO';
  l_sql_stmt VARCHAR2(4000);
BEGIN
  l_sql_stmt := 'CREATE OR REPLACE TRIGGER trg_' || l_tbl_name ||
                '  BEFORE UPDATE ON ' || l_tbl_name ||
                '  FOR EACH ROW ' || 
                'BEGIN ';
  FOR cols IN (SELECT *
                 FROM user_tab_cols
                WHERE table_name = l_tbl_name )
  LOOP
    l_sql_stmt := l_sql_stmt ||
                  ' IF UPDATING( ''' || cols.column_name || ''' ) ' ||
                  ' THEN ' ||
                  '   dbms_output.put_line( :new.' || cols.column_name || '); ' ||
                  ' END IF; ';
  END LOOP;
  l_sql_stmt := l_sql_stmt || ' END; ';
  dbms_output.put_line( l_sql_stmt );
  EXECUTE IMMEDIATE l_sql_stmt;
END;

答案 1 :(得分:2)

          :old            :new
=======================================================
insert | null         |    new value to be inserted
update | old value    |    new value to be updated
delete | old value    |    null
=======================================================

以上值是参考行级触发器引用的,这意味着每次触发都会触发每行。

上表显示了相对于上述dml语句的new old值,让我们举个例子。

Insert into abc (1,'gaurav soni',pune);--empid,name,city

假设我在表abc上有一个触发器,当插入表abc时,检查该位置表中是否存在该城市(我知道我们可以使用外键约束来执行此操作)但这是一个示例,所以我们可以将城市称为:new.city,但我们无法在插入时引用:old .city,因为其值为null

如果是update,我们可以同时引用:new and :old value

如果delete old valuenew value只能引用null deleting :new delete

注意:在:old的情况下使用insertingcompile time error run time error的{​​{1}}将不会为您INSERTING,DELETING ,但肯定会繁荣并给你{{1}} 您可以使用{{1}}子句来避免这种情况。