如何在Oracle中存储变更历史数据和处理未变更的值
How to store changes history data in Oracle and handle the non-changed values
大家好,我有一个 table 叫用户:
然后我有另一个 table 保存对上述 table 所做的更改,称为 Users_History_Changes:
我知道我需要触发器,它会在 table 更新并在 Users_History_Changes table 中插入值时触发。但这是我做不到的事情。当在 Users_History_Changes table 中创建日志并且仅更新 Last_Name 时,其他字段必须保留为空。然后,First_Name 发生了变化,因此 table 仅显示了这一点。最后我们改变年龄,ID = 1 的用户变成 from 'Raul Peres, 25' to 'Pedro Felipes, 30' . Time_Stamp 是进行更改的时间。
您可以测试每个值是否已更改为插入历史记录的一部分table:
create trigger users_trigger
before insert or update on users
for each row
begin
insert into users_history_changes (id, first_name, last_name, age, timestamp_changes)
values (:new.id,
case when :old.first_name is null or :new.first_name != :old.first_name then :new.first_name end,
case when :old.last_name is null or :new.last_name != :old.last_name then :new.last_name end,
case when :old.age is null or :new.age != :old.age then :new.age end,
systimestamp);
end;
/
检查值是否已更改 为 可能没有用,因为这不是很有用的历史记录,所以我只检查了 来自 空。甚至那些测试 can/should 也可以扩展到涵盖边缘情况,例如检查某些东西是否真的改变了。
无论如何,用下面的语句:
insert into users
select 1, 'Raul', 'Peres', 25 from dual
union all select 2, 'Francis', 'Lotters', 40 from dual
union all select 3, 'Maria', 'Lopez', 39 from dual;
update users set last_name = 'Felipes' where id = 1;
update users set first_name = 'Pedro' where id = 1;
update users set age = 30 where id = 1;
update users set first_name = 'Maria', last_name = 'Sanchez', age = 40 where id = 3;
历史 table 结束于:
ID FIRST_NAME LAST_NAME AGE TIMESTAMP_CHANGES
---------- ---------- ---------- ---------- ----------------------------
1 Raul Peres 25 19-JUN-19 20.02.33.470409000
2 Francis Lotters 40 19-JUN-19 20.02.33.473139000
3 Maria Lopez 39 19-JUN-19 20.02.33.473183000
1 Felipes 19-JUN-19 20.02.33.548101000
1 Pedro 19-JUN-19 20.02.33.594305000
1 30 19-JUN-19 20.02.33.640293000
3 Sanchez 40 19-JUN-19 20.02.33.688710000
或者,如果您希望每个更改的值占一行,您可以使用 updating
子句:
create trigger users_trigger
before insert or update on users
for each row
begin
if inserting then
insert into users_history_changes (id, first_name, last_name, age, timestamp_changes)
values (:new.id, :new.first_name, :new.last_name, :new.age, systimestamp);
end if;
if updating ('FIRST_NAME') then
insert into users_history_changes (id, first_name, timestamp_changes)
values (:new.id, :new.first_name, systimestamp);
end if;
if updating ('LAST_NAME') then
insert into users_history_changes (id, last_name, timestamp_changes)
values (:new.id, :new.last_name, systimestamp);
end if;
if updating ('AGE') then
insert into users_history_changes (id, age, timestamp_changes)
values (:new.id, :new.age, systimestamp);
end if;
end;
/
但是这将为 'changed' 相同值的值生成一行,除非您添加进一步的逻辑来检查实际更改 - updating()
检查是更新语句包含set
列表中的列。 db<>fiddle
您可以比较更新日志记录的新旧值
create or replace trigger trg_users on users
after insert or update
for each row
declare
v_dml_type varchar2(1);
procedure pr_upd_log( i_dml_type varchar2, i_col_name varchar2,
i_old_val varchar2, i_new_val varchar2 ) is
begin
insert into Users_History_Changes( dml_type, col_name, old_val, new_val )
values( i_dml_type, i_col_name, i_old_val, i_new_val );
end;
begin
if updating then v_dml_type := 'U';
elsif inserting then v_dml_type := 'I'; end if;
if ( nvl(:old.user_id,-987) != nvl(:new.user_id,-987) ) then
pr_upd_log(v_dml_type,'user_id',to_char(:old.user_id),to_char(:new.user_id));
end if;
if ( nvl(:old.age,-987) != nvl(:new.age,-987) ) then
pr_upd_log(v_dml_type,'age',to_char(:old.age),to_char(:new.age));
end if;
if ( nvl(:old.first_name,'NuLLxYZ') != nvl(:new.first_name,'NuLLxYZ') ) then
pr_upd_log(v_dml_type,'first_name',:old.first_name,:new.first_name);
end if;
if ( nvl(:old.last_name,'NuLLxYZ') != nvl(:new.last_name,'NuLLxYZ') ) then
pr_upd_log(v_dml_type,'last_name',:old.last_name,:new.last_name);
end if;
-- for v_dml_type = 'I', use your former way
end;
大家好,我有一个 table 叫用户:
然后我有另一个 table 保存对上述 table 所做的更改,称为 Users_History_Changes:
我知道我需要触发器,它会在 table 更新并在 Users_History_Changes table 中插入值时触发。但这是我做不到的事情。当在 Users_History_Changes table 中创建日志并且仅更新 Last_Name 时,其他字段必须保留为空。然后,First_Name 发生了变化,因此 table 仅显示了这一点。最后我们改变年龄,ID = 1 的用户变成 from 'Raul Peres, 25' to 'Pedro Felipes, 30' . Time_Stamp 是进行更改的时间。
您可以测试每个值是否已更改为插入历史记录的一部分table:
create trigger users_trigger
before insert or update on users
for each row
begin
insert into users_history_changes (id, first_name, last_name, age, timestamp_changes)
values (:new.id,
case when :old.first_name is null or :new.first_name != :old.first_name then :new.first_name end,
case when :old.last_name is null or :new.last_name != :old.last_name then :new.last_name end,
case when :old.age is null or :new.age != :old.age then :new.age end,
systimestamp);
end;
/
检查值是否已更改 为 可能没有用,因为这不是很有用的历史记录,所以我只检查了 来自 空。甚至那些测试 can/should 也可以扩展到涵盖边缘情况,例如检查某些东西是否真的改变了。
无论如何,用下面的语句:
insert into users
select 1, 'Raul', 'Peres', 25 from dual
union all select 2, 'Francis', 'Lotters', 40 from dual
union all select 3, 'Maria', 'Lopez', 39 from dual;
update users set last_name = 'Felipes' where id = 1;
update users set first_name = 'Pedro' where id = 1;
update users set age = 30 where id = 1;
update users set first_name = 'Maria', last_name = 'Sanchez', age = 40 where id = 3;
历史 table 结束于:
ID FIRST_NAME LAST_NAME AGE TIMESTAMP_CHANGES
---------- ---------- ---------- ---------- ----------------------------
1 Raul Peres 25 19-JUN-19 20.02.33.470409000
2 Francis Lotters 40 19-JUN-19 20.02.33.473139000
3 Maria Lopez 39 19-JUN-19 20.02.33.473183000
1 Felipes 19-JUN-19 20.02.33.548101000
1 Pedro 19-JUN-19 20.02.33.594305000
1 30 19-JUN-19 20.02.33.640293000
3 Sanchez 40 19-JUN-19 20.02.33.688710000
或者,如果您希望每个更改的值占一行,您可以使用 updating
子句:
create trigger users_trigger
before insert or update on users
for each row
begin
if inserting then
insert into users_history_changes (id, first_name, last_name, age, timestamp_changes)
values (:new.id, :new.first_name, :new.last_name, :new.age, systimestamp);
end if;
if updating ('FIRST_NAME') then
insert into users_history_changes (id, first_name, timestamp_changes)
values (:new.id, :new.first_name, systimestamp);
end if;
if updating ('LAST_NAME') then
insert into users_history_changes (id, last_name, timestamp_changes)
values (:new.id, :new.last_name, systimestamp);
end if;
if updating ('AGE') then
insert into users_history_changes (id, age, timestamp_changes)
values (:new.id, :new.age, systimestamp);
end if;
end;
/
但是这将为 'changed' 相同值的值生成一行,除非您添加进一步的逻辑来检查实际更改 - updating()
检查是更新语句包含set
列表中的列。 db<>fiddle
您可以比较更新日志记录的新旧值
create or replace trigger trg_users on users
after insert or update
for each row
declare
v_dml_type varchar2(1);
procedure pr_upd_log( i_dml_type varchar2, i_col_name varchar2,
i_old_val varchar2, i_new_val varchar2 ) is
begin
insert into Users_History_Changes( dml_type, col_name, old_val, new_val )
values( i_dml_type, i_col_name, i_old_val, i_new_val );
end;
begin
if updating then v_dml_type := 'U';
elsif inserting then v_dml_type := 'I'; end if;
if ( nvl(:old.user_id,-987) != nvl(:new.user_id,-987) ) then
pr_upd_log(v_dml_type,'user_id',to_char(:old.user_id),to_char(:new.user_id));
end if;
if ( nvl(:old.age,-987) != nvl(:new.age,-987) ) then
pr_upd_log(v_dml_type,'age',to_char(:old.age),to_char(:new.age));
end if;
if ( nvl(:old.first_name,'NuLLxYZ') != nvl(:new.first_name,'NuLLxYZ') ) then
pr_upd_log(v_dml_type,'first_name',:old.first_name,:new.first_name);
end if;
if ( nvl(:old.last_name,'NuLLxYZ') != nvl(:new.last_name,'NuLLxYZ') ) then
pr_upd_log(v_dml_type,'last_name',:old.last_name,:new.last_name);
end if;
-- for v_dml_type = 'I', use your former way
end;