常に呼び出されることに関する当面の問題は、関連する列名を検索するのではなく、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
したがって、実行されたアクションごとに1つの監査行、2つの挿入、および3つの更新があります。ただし、変更された列ごとに個別のデータを表示する必要があります。
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つの更新になりました。3つのupdateステートメントは、変更された5つの列を示しています。これを頻繁に使用する場合は、それをビューにすることを検討してください。
それでは、それを少しだけ分解してみましょう。コアはこの内部選択であり、これを使用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
これで、列を持つ一時的なビューができました。この場合、列が3つしかないため、監査テーブルの行数は3倍になります。最後に、値が変更された行、つまりwhere value != prev_value
(nullを許可)のみを含むように表示する外部選択フィルター。
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
は、監査テーブルのエントリもピボットされていないビューで3行に変換され、最初のcase
句の3つすべてに同じテキストが表示されているためです。