【问题标题】:PL SQL trigger to insert history record when a column is updated更新列时插入历史记录的 PL SQL 触发器
【发布时间】:2012-03-06 02:09:48
【问题描述】:

任何列在表中更新时,我想在历史表中插入一行。

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

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

我熟悉触发器以及如何捕获一列的更新。我正在专门寻找如何编写 one 触发器,该触发器将记录插入到历史表中的 any 列中,该列在历史表的相应表中得到更新。

编辑 1
我已经在我的帖子中声明了 NOWHERE,我正在寻找源代码,这对那些不赞成我并认为我正在寻找源代码的人感到羞耻。你可以查看我之前的问题/答案,看看我不是在寻找“免费源代码”。

正如我在最初的问题中所说,我正在寻找如何来写这个。我已经检查了http://plsql-tutorial.com/plsql-triggers.htm,并且有一个代码块显示了如何为更新 ONE 列时编写触发器。我想也许有人会知道如何为我提出的场景提供更通用的触发器。

【问题讨论】:

  • 对。想知道我每小时收费多少? SO 不是免费的编码服务。到目前为止,您尝试过什么?
  • @Adrian - 只是在这里寻找一点方向......我的问题从来没有说我正在寻找任何人为我编写代码......
  • 很公平。问题是您的问题听起来就像我现在在工作中阅读的规范文档。你必须给我们一些东西。你有什么办法吗?你有没有研究过这是否可能?您是否研究过 RDBMS (oracle) 是否已提供该功能?
  • @Adrian - 我不确定这是否可能,但我在 PL SQL 中考虑可能有一种方法可以遍历特定表中的列并检索它们的名称在触发器中与 ":old" 和 ":new" 一起使用。

标签: sql oracle triggers


【解决方案1】:

假设一个普通表而不是一个对象表,你没有很多选择。你的触发器必须是某种形式

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;

您可以从数据字典 (USER_TAB_COLS) 中获取 COLUMN1COLUMN2、...COLUMN&lt;&lt;n&gt;&gt; 字符串,而不是对它们进行硬编码,但您仍然需要对引用进行硬编码:new 伪记录中的列。

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

不过,更一般地说,我会质疑以这种方式存储数据是否明智。为每行修改的每一列在历史表中存储一行,这使得使用历史数据非常具有挑战性。如果有人想知道特定行在特定时间点处于什么状态,则必须将历史记录表连接到自身 N 次,其中 N 是该时间点表中的列数。这将是非常低效的,这很快就会使人们避免尝试使用历史数据,因为他们无法在合理的时间内用它做有用的事情而不把头发扯下来。拥有一个与活动表具有相同列集的历史表(为跟踪日期等添加更多列)并在每次更新行时在历史表中插入一行通常会更有效。这会占用更多空间,但通常更容易使用。

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

【讨论】:

    【解决方案2】:

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

    试试这个(未经测试):

    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;
    /
    

    【讨论】:

    • +1 - 感谢这篇文章 - 我可能最终会朝着这个方向发展,因为它干净直接
    【解决方案3】:

    请注意,根据您的设计,如果空间有限,您可以创建一个视图,以跟踪您打算采用的方式所做的更改,并仅显示当时的记录。

    【讨论】:

    • 这似乎是一条评论,应该在问题下发布。在拥有评论权限之前,您需要更多地参与社区以获得一些声誉。欢迎来到 SO!
    猜你喜欢
    • 2012-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 2013-10-05
    • 1970-01-01
    • 2016-01-30
    相关资源
    最近更新 更多