如何使用触发器将基表的所有更新列添加到审计表的多行?

Posted

技术标签:

【中文标题】如何使用触发器将基表的所有更新列添加到审计表的多行?【英文标题】:how to use trigger in a way to have all updated columns of base table to multiple rows of audit table? 【发布时间】:2019-06-21 13:23:04 【问题描述】:

我正在尝试将“基本”表的旧值和新值插入到审计表“审计”中。 问题在于如何将数据存储在审计表中。 如果更新了不止一列,我应该能够以这种格式存储它。

“审计”表的结构

Id | Column_Name | Old_Value | New_Value
 1 | Strike      | 4         | 5
 2 | Spare       | 3         | 7
 3 | Score       | 10        | 18

我的解决方案给出了以下结构

Id | old_strike | new_strike | old_spare | new_spare | old_score | new_score
1  | 4          | 5          | 3         | 7         | 10        | 18

我尝试过使用触发器将新旧值保存在一行中,但很难弄清楚如何遍历每列的单个插入语句。

AFTER INSERT OR UPDATE OR DELETE ON frame  -- after event
FOR EACH ROW    // fires for each record
BEGIN

INSERT INTO frame_audit(bowler_id,game_id,frame_number,
old_strike,new_strike,
old_spare,new_spare,
old_score,new_score,
change_date,operation)

VALUES(:NEW.bowler_id,:NEW.game_id,:NEW.frame_number,
:OLD.strike,:NEW.strike,
:OLD.spare,:NEW.spare,
:OLD.score,:NEW.score,
SYSDATE,'UPDATE');
END audit_frames```

// Structure of 'Audit' table
Id | Column_Name | Old_Value | New_Value
 1 | Strike      | 4         | 5
 2 | Spare       | 3         | 7
 3 | Score       | 10        | 18

【问题讨论】:

没有动态读取列的新旧值的选项,您必须为所有列显式插入行 每列一行是一种熟悉的审计反模式。这似乎是明智的,但这是一场性能噩梦。更新表中的一行在审计中插入三行?母马。十列表这么十个插入?母马。只需审核已更改的列?祝你好运重新组装唱片的更改历史。另一个'母马。但是,如果您喜欢该结构,请确保您的 AUDIT 表具有 TRANSACTION_ID 列来标识一次更改的所有行。否则你将永远无法重建记录的状态。 【参考方案1】:

正如在 cmets 中提到的,没有选项可以动态获取特定于每个列名称的 OLDNEW 值。您所能做的就是为所有具有硬编码列名的列编写显式插入。

 ...
 ...

BEGIN


INSERT INTO frame_audit (Id ,Column_Name , Old_Value , New_Value)
SELECT  audit_seq.nextval,'bowler_id',old.bowler_id,new.bowler_id 
FROM DUAL WHERE 
 (    old.bowler_id <> new.bowler_id OR
      (old.bowler_id is NULL     and new.bowler_id is NOT NULL) OR
      (old.bowler_id is NOT NULL and new.bowler_id is NULL   ) 
 );

INSERT INTO frame_audit (Id ,Column_Name , Old_Value , New_Value)
SELECT  audit_seq.nextval,'game_id',old.game_id,new.game_id 
FROM DUAL WHERE 
 (    old.game_id <> new.game_id OR
      (old.game_id is NULL     and new.game_id is NOT NULL) OR
      (old.game_id is NOT NULL and new.game_id is NULL   ) 
 );

--similar inserts for all other columns


 ...

END;

这里我假设您创建了一个序列来为审计表生成 id。

您可以使用NVLCOALESCE 简化冗长的NULL 校验表达式,但您应该根据每列的数据类型使用它。如果列定义为not null,则简单的&lt;&gt; 检查就足够了。对于DATE/TIMESTAMP 相关的列,如果您想以所需的格式存储它,则需要使用TO_CHAR

【讨论】:

以上是关于如何使用触发器将基表的所有更新列添加到审计表的多行?的主要内容,如果未能解决你的问题,请参考以下文章

在 Mysql 中添加新列时如何轻松维护审计触发器

如果 Column_X 更新,PLSQL 触发器记录对审计表的更新

将基表值与第二个表的值之和与group by进行比较

表的审计跟踪

如何将基表中的列与基于用户注册创建的用户表结合起来

更新表的多行的最快方法是啥