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つの更新ステートメントは、変更された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倍になります。最後に、値が変更された行のみを含むように表示する外側の選択フィルター、つまり 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つすべてに同じテキストが表示されます。 条項。