如何使用触发器将基础 table 的所有更新列添加到多行审计 table?
how to use trigger in a way to have all updated columns of base table to multiple rows of audit table?
我正在尝试将 'Base' table 的旧值和新值插入审计 table 'Audit'。
问题在于如何将数据存储在 Audit table 中。
如果更新了不止一列,我应该能够以这种格式存储它。
'Audit'table
的结构
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
如评论所述,没有选项可以动态获取特定于每个列名称的 OLD
和 NEW
值。您所能做的就是为所有具有硬编码列名的列编写显式插入。
...
...
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 table。
您可以使用 NVL
或 COALESCE
简化冗长的 NULL
检查表达式,但您应该根据每列的数据类型使用它。如果列定义为 not null
,一个简单的 <>
检查就足够了。对于 DATE/TIMESTAMP
相关列,如果您想以所需格式存储它,则需要使用 TO_CHAR
。
我正在尝试将 'Base' table 的旧值和新值插入审计 table 'Audit'。 问题在于如何将数据存储在 Audit table 中。 如果更新了不止一列,我应该能够以这种格式存储它。
'Audit'table
的结构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
如评论所述,没有选项可以动态获取特定于每个列名称的 OLD
和 NEW
值。您所能做的就是为所有具有硬编码列名的列编写显式插入。
...
...
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 table。
您可以使用 NVL
或 COALESCE
简化冗长的 NULL
检查表达式,但您应该根据每列的数据类型使用它。如果列定义为 not null
,一个简单的 <>
检查就足够了。对于 DATE/TIMESTAMP
相关列,如果您想以所需格式存储它,则需要使用 TO_CHAR
。