Ihr unmittelbares Problem mit dem else
immer aufgerufen wird, weil Sie Ihre Indexvariable r
verwenden direkt, anstatt nach dem relevanten Spaltennamen zu suchen:
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;
Sie zeigen auch nur eine id
an Spalte in Ihrer Tabellenerstellung, also wenn r
ist 2
, es wird immer sagen, dass es name
einfügt , nie aktualisieren. Noch wichtiger, wenn Sie einen Namen
hatten Spalte und haben diese nur für eine bestimmte id
aktualisiert , würde dieser Code die id
anzeigen als Einfügen, wenn es sich nicht geändert hatte. Sie müssen die Einfügung/Aktualisierung in separate Blöcke aufteilen:
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;
Dies wird immer noch sagen, dass es name
einfügt auch wenn die Spalte nicht existiert, aber ich nehme an, das ist ein Fehler, und ich vermute, Sie würden versuchen, die Liste der Namen aus user_tab_columns
zu füllen jedenfalls, wenn Sie wirklich versuchen wollen, es dynamisch zu machen.
Ich stimme (zumindest einigen) den anderen zu, dass Sie wahrscheinlich besser dran wären, wenn Sie eine Audit-Tabelle verwenden würden, die eine Kopie der gesamten Zeile anstelle einzelner Spalten erstellt. Ihr Einwand scheint die Komplikation zu sein, die geänderten Spalten einzeln aufzulisten. Sie könnten diese Informationen mit ein wenig Arbeit immer noch erhalten, indem Sie die Audit-Tabelle entpivotieren, wenn Sie spaltenweise Daten benötigen. Zum Beispiel:
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
Sie haben also eine Audit-Zeile für jede durchgeführte Aktion, zwei Einfügungen und drei Aktualisierungen. Sie möchten jedoch separate Daten für jede geänderte Spalte sehen.
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
Aus den fünf Audit-Aufzeichnungen sind sieben Aktualisierungen geworden; Die drei Aktualisierungsanweisungen zeigen die fünf geänderten Spalten. Wenn Sie dies häufig verwenden, sollten Sie es in eine Ansicht umwandeln.
Also lasst uns das ein bisschen aufschlüsseln. Der Kern ist diese innere Auswahl, die lag()
um den vorherigen Wert der Zeile aus dem vorherigen Audit-Datensatz für diese id
zu erhalten :
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
Das gibt uns eine temporäre Ansicht, die alle Audit-Tabellenspalten plus die Verzögerungsspalte enthält, die dann für den unpivot()
Operation, die Sie verwenden können, da Sie die Frage als 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')
)
Jetzt haben wir eine temporäre Ansicht mit id, action, when, col, value, prev_value
Säulen; Da ich in diesem Fall nur drei Spalten habe, hat das die dreifache Anzahl von Zeilen in der Audit-Tabelle. Schließlich filtert die äußere Auswahl diese Ansicht so, dass sie nur die Zeilen enthält, in denen sich der Wert geändert hat, d. h. wo value !=prev_value
(unter Berücksichtigung von Nullen).
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)
Ich verwende case
einfach etwas ausdrucken, aber Sie können natürlich mit den Daten machen, was Sie wollen. Der distinct
wird benötigt, weil insert
Einträge in der Audit-Tabelle werden auch in der nicht-pivozierten Ansicht in drei Zeilen konvertiert, und ich zeige denselben Text für alle drei aus meinem ersten case
Klausel.