PL SQL触发器在更新列时插入历史记录

时间:2012-02-14 17:29:54

标签: sql oracle triggers

我希望在表格中更新任何列时在历史记录表中插入一行。

我只想捕获列名,旧值和新值。

我希望这个触发器尽可能可重用,因为我将在其他表上使用相同的概念。

我熟悉触发器以及如何捕获一列上的更新。我特意寻找如何编写一个触发器,将一条记录插入历史表中,以便在历史表的相应表中更新任何列。

编辑1
我在帖子中说过 NOWHERE 我正在寻找源代码,所以对任何那些贬低我并认为我正在寻找的人感到羞耻。您可以查看我以前的问题/答案,看看我不是在寻找“免费源代码”。

正如我在原始问题中所述,我正在寻找如何来写这个。我已经检查了http://plsql-tutorial.com/plsql-triggers.htm,并且有一个代码块,显示了如何在更新ONE列时编写触发器。我想也许有人会有专门知识来指导我为我所呈现的场景设置一个更通用的触发器。

3 个答案:

答案 0 :(得分:10)

假设一个常规表而不是一个对象表,你没有很多选项。你的触发器必须是

形式
CREATE OR REPLACE TRIGGER trigger_name
  AFTER UPDATE ON table_name
  FOR EACH ROW
BEGIN
  IF( UPDATING( 'COLUMN1' ) )
  THEN
    INSERT INTO log_table( column_name, column_value )
      VALUES( 'COLUMN1', :new.column1 );
  END IF;

  IF( UPDATING( 'COLUMN2' ) )
  THEN
    INSERT INTO log_table( column_name, column_value )
      VALUES( 'COLUMN2', :new.column2 );
  END IF;

  <<repeat for all columns>>
END;

您可以从数据字典(COLUMN1)中提取COLUMN2COLUMN<<n>>,... USER_TAB_COLS字符串,而不是硬编码,但您仍然可以必须对:new伪记录中的列的引用进行硬编码。

您可以通过查询数据字典(最有可能是USER_TAB_COLSALL_TAB_COLS),使用DDL语句构建字符串,然后执行{,编写一段生成上述触发器的代码。 {1}}执行DDL语句。然后,只要将新列添加到任何表中以重新创建该列的触发器,就必须调用此脚本。编写和调试这种DDL生成代码很繁琐,但在技术上并不特别具有挑战性。但它很少值得,因为有人不可避免地添加了一个新列并忘记重新运行脚本,或者有人需要修改触发器以执行一些额外的工作,并且更容易手动更新触发器而不是修改和测试生成的脚本触发器。

但更一般地说,我会质疑以这种方式存储数据的智慧。在历史表中为修改的每一行的每一列存储一行使得使用历史数据非常具有挑战性。如果有人想知道特定时间点特定行所处的状态,则必须将历史表连接到自身N次,其中N是该时间点表中的列数。这将是非常低效的,很快就会让人们避免尝试使用历史数据,因为他们不能在合理的时间内用它做有用的东西而不会撕掉他们的头发。通常使用具有与活动表相同的列集的历史表(添加一些用于跟踪日期等)的历史表以及每次更新行时在历史表中插入一行通常更有效。这将消耗更多空间,但通常更容易使用。

Oracle有多种方法可以审核数据更改 - 您可以EXECUTE IMMEDIATE DML,可以使用细粒度审核(FGA),可以使用Workspace Manager,也可以使用Oracle Total Recall。如果您正在寻找比编写自己的触发器代码更多的灵活性,我强烈建议您研究一些本质上更加自动化的其他技术,而不是尝试开发自己的架构。

答案 1 :(得分:4)

您可以将历史记录表设置为SAME作为主表,+日期和类型字段。您只需要捕获旧值,因为新值在主表中。

试试这个(未经测试):

create or replace trigger "MY_TRIGGER"
before update or delete
on MY_TABLE referencing new as new old as old
for each row
declare
  l_dml_type varchar2(10);
begin
if (updating) then
  l_dml_type := 'UPD';
else
  l_dml_type := 'DEL';
end if;

insert into MY_TABLE_HIST
(
 col1,
 col2,
 col3,
 dml_type,
 dml_date
)
values
(
 :old.col1,
 :old.col2,
 :old.col3,
 l_dml_type,
 sysdate
);
end;
/

答案 2 :(得分:1)

作为一个注释,根据您的设计,如果空间是一个限制,您可以创建一个视图,以跟踪您的方式的变化,并只显示当时的记录。