总是调用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;
您在创建表时也只显示了一个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;
即使该列不存在,它仍然会说它正在插入 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
因此,对于所采取的每项操作,您都有一个审核行,两个插入和三个更新。但是您希望看到更改的每一列的单独数据。
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
5 条审计记录变成了 7 条更新;三个更新语句显示修改的五列。如果你会经常使用它,你可以考虑把它变成一个视图。
所以让我们稍微分解一下。核心是这个内部选择,它使用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
这为我们提供了一个临时视图,其中包含所有审计表列和滞后列,然后用于 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')
)
现在我们有了一个包含id, action, when, col, value, prev_value 列的临时视图;在这种情况下,因为我只有三列,它的行数是审计表中的三倍。最后,外部选择过滤器仅包含值已更改的行,即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)
我使用case 只是打印一些东西,但当然你可以对数据做任何你想做的事情。 distinct 是必需的,因为审计表中的 insert 条目也在非透视视图中转换为三行,并且我在第一个 case 子句中为所有三行显示相同的文本。