Gau*_*oni 2 oracle triggers oracle11g
我需要建立一个trigger以oracle 11g用于审核表.
我有一张表50 columns需要audited.
every new insert表格,我需要输入一个条目audit table (1 row).every update,假设我更新1st 2nd column,那么它将在审计中创建两个记录 old value and new value.审计表的结构将是
id NOT NULL
attribute NOT NULL
OLD VALUE NOT NULL
NEW VALUE NOT NULL
cre_date NOT NULL
upd_date NULL
cre_time NOT NULL
upd_time NULL
Run Code Online (Sandbox Code Playgroud)
在情况下insert,只有主键(主表),即中id 和cre_date and cre_time需要填充和attribute等于*,在更新的情况下,假设可乐COLB正在更新,那么所有需要被populated.In这种情况下,两个记录将被创建第一条记录的属性colA和相应的old and new值,并且相同colB
现在我的审计解决方案是not very optimized,我创建了一个row level trigger,它将检查该表的每一个50列,无论它是否changed基于它new and old value(if -else),它将填充审计表.我对我的解释不满意,这就是我在这里发帖的原因.我在下面的链接中看到的另一个解决方案:
http://stackoverflow.com/questions/1421645/oracle-excluding-updates-of-one-column-for-firing-a-trigger
这在我的情况下不起作用,我为此做了一个POC,如下所示:
create table temp12(id number);
create or replace trigger my_trigger
after update or insert on temp12
for each row
declare
TYPE tab_col_nt IS table of varchar2(30);
v_tab_col_nt tab_col_nt;
begin
v_tab_col_nt := tab_col_nt('id','name');
for r in v_tab_col_nt.first..v_tab_col_nt.last
loop
if updating(r) then
insert into data_table values(1,'i am updating'||r);
else
insert into data_table values(2,'i am inserting'||r);
end if;
end loop;
end;
Run Code Online (Sandbox Code Playgroud)
如果更新它是调用else部分我不知道为什么.这可以通过compound trigger
else始终被调用的直接问题是因为您r直接使用索引变量,而不是查找相关的列名:
for r in v_tab_col_nt.first..v_tab_col_nt.last
loop
if updating(v_tab_col_nt(r)) then
insert into data_table values(1,'i am updating '||v_tab_col_nt(r));
else
insert into data_table values(2,'i am inserting '||v_tab_col_nt(r));
end if;
end loop;
Run Code Online (Sandbox Code Playgroud)
你也只显示了id在表中创建列,所以,当r是2时,总是会说这是插入name,更新从来没有.更重要的是,如果你确实有一个name列并且只为给定的更新id,那么这段代码会id在未更改时显示为插入.您需要将插入/更新拆分为单独的块:
if updating then
for r in v_tab_col_nt.first..v_tab_col_nt.last loop
if updating(v_tab_col_nt(r)) then
insert into data_table values(1,'i am updating '||v_tab_col_nt(r));
end if;
end loop;
else /* inserting */
for r in v_tab_col_nt.first..v_tab_col_nt.last loop
insert into data_table values(2,'i am inserting '||v_tab_col_nt(r));
end loop;
end if;
Run Code Online (Sandbox Code Playgroud)
name即使该列不存在,这仍然会说它正在插入,但我认为这是一个错误,我想user_tab_columns如果你真的想让它变得动态,那么你无论如何都要尝试填充名单.
我同意(至少其中一些)其他人,你可能会更好地使用一个审计表来获取整行的副本,而不是单个列.您的异议似乎是单独列出哪些列更改的复杂性.通过一些工作,您仍然可以通过在需要逐列数据时取消忽略审计表来获取此信息.例如:
create table temp12(id number, col1 number, col2 number, col3 number);
create table temp12_audit(id number, col1 number, col2 number, col3 number,
action char(1), when timestamp);
create or replace trigger temp12_trig
before update or insert on temp12
for each row
declare
l_action char(1);
begin
if inserting then
l_action := 'I';
else
l_action := 'U';
end if;
insert into temp12_audit(id, col1, col2, col3, action, when)
values (:new.id, :new.col1, :new.col2, :new.col3, l_action, systimestamp);
end;
/
insert into temp12(id, col1, col2, col3) values (123, 1, 2, 3);
insert into temp12(id, col1, col2, col3) values (456, 4, 5, 6);
update temp12 set col1 = 9, col2 = 8 where id = 123;
update temp12 set col1 = 7, col3 = 9 where id = 456;
update temp12 set col3 = 7 where id = 123;
select * from temp12_audit order by when;
ID COL1 COL2 COL3 A WHEN
---------- ---------- ---------- ---------- - -------------------------
123 1 2 3 I 29/06/2012 15:07:47.349
456 4 5 6 I 29/06/2012 15:07:47.357
123 9 8 3 U 29/06/2012 15:07:47.366
456 7 5 9 U 29/06/2012 15:07:47.369
123 9 8 7 U 29/06/2012 15:07:47.371
Run Code Online (Sandbox Code Playgroud)
因此,对于每个采取的操作,两个插入和三个更新,您有一个审计行.但是,您希望查看每个更改的列的单独数据.
select distinct id, when,
case
when action = 'I' then 'Record inserted'
when prev_value is null and value is not null
then col || ' set to ' || value
when prev_value is not null and value is null
then col || ' set to null'
else col || ' changed from ' || prev_value || ' to ' || value
end as change
from (
select *
from (
select id,
col1, lag(col1) over (partition by id order by when) as prev_col1,
col2, lag(col2) over (partition by id order by when) as prev_col2,
col3, lag(col3) over (partition by id order by when) as prev_col3,
action, when
from temp12_audit
)
unpivot ((value, prev_value) for col in (
(col1, prev_col1) as 'col1',
(col2, prev_col2) as 'col2',
(col3, prev_col3) as 'col3')
)
)
where value != prev_value
or (value is null and prev_value is not null)
or (value is not null and prev_value is null)
order by when, id;
ID WHEN CHANGE
---------- ------------------------- -------------------------
123 29/06/2012 15:07:47.349 Record inserted
456 29/06/2012 15:07:47.357 Record inserted
123 29/06/2012 15:07:47.366 col1 changed from 1 to 9
123 29/06/2012 15:07:47.366 col2 changed from 2 to 8
456 29/06/2012 15:07:47.369 col1 changed from 4 to 7
456 29/06/2012 15:07:47.369 col3 changed from 6 to 9
123 29/06/2012 15:07:47.371 col3 changed from 3 to 7
Run Code Online (Sandbox Code Playgroud)
五项审计记录已变为七项更新; 三个更新语句显示修改了五列.如果你要经常使用它,你可以考虑将它变成一个视图.
所以让我们稍微打破一下.核心是这个内部选择,用于lag()从上一个审计记录中获取该行的先前值id:
select id,
col1, lag(col1) over (partition by id order by when) as prev_col1,
col2, lag(col2) over (partition by id order by when) as prev_col2,
col3, lag(col3) over (partition by id order by when) as prev_col3,
action, when
from temp12_audit
Run Code Online (Sandbox Code Playgroud)
这为我们提供了一个临时视图,其中包含所有审计表列和滞后列,然后用于unpivot()操作,您可以使用该标记将问题标记为11g:
select *
from (
...
)
unpivot ((value, prev_value) for col in (
(col1, prev_col1) as 'col1',
(col2, prev_col2) as 'col2',
(col3, prev_col3) as 'col3')
)
Run Code Online (Sandbox Code Playgroud)
现在我们有一个临时视图,其中包含id, action, when, col, value, prev_value列; 在这种情况下,因为我只有三列,它具有审计表中行数的三倍.最后,外部选择过滤器查看仅包含值已更改的行,即where value != prev_value(允许空值).
select
...
from (
...
)
where value != prev_value
or (value is null and prev_value is not null)
or (value is not null and prev_value is null)
Run Code Online (Sandbox Code Playgroud)
我case只是打印一些东西,但当然你可以用数据做任何你想做的事情.这distinct是必需的,因为insert审计表中的条目也在unpivoted视图中转换为三行,并且我在第一个子句中显示了所有三个相同的文本case.
| 归档时间: |
|
| 查看次数: |
8672 次 |
| 最近记录: |