Il tuo problema immediato con else
essere sempre chiamato è perché stai usando la tua variabile di indice r
direttamente, invece di cercare il nome della colonna pertinente:
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;
Stai anche mostrando solo un id
colonna nella creazione della tabella, quindi quando r
è 2
, dirà sempre che sta inserendo name
, mai aggiornato. Ancora più importante, se avevi un name
colonna e lo stavo aggiornando solo per un determinato id
, questo codice mostrerebbe l'id
come inserimento quando non era cambiato. Devi dividere l'inserimento/l'aggiornamento in blocchi separati:
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;
Questo dirà ancora che sta inserendo name
anche se la colonna non esiste, ma presumo che sia un errore e immagino che proveresti a popolare l'elenco di nomi da user_tab_columns
comunque se proprio vuoi provare a renderlo dinamico.
Sono d'accordo con (almeno con alcuni) gli altri che probabilmente staresti meglio con una tabella di controllo che prende una copia dell'intera riga, piuttosto che singole colonne. La tua obiezione sembra essere la complicazione dell'elencare individualmente quali colonne sono state modificate. È comunque possibile ottenere queste informazioni, con un po' di lavoro, annullando il pivot della tabella di controllo quando sono necessari dati colonna per colonna. Ad esempio:
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
Quindi hai una riga di controllo per ogni azione intrapresa, due inserimenti e tre aggiornamenti. Ma vuoi vedere dati separati per ogni colonna che è cambiata.
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
I cinque record di audit si sono trasformati in sette aggiornamenti; le tre istruzioni di aggiornamento mostrano le cinque colonne modificate. Se lo utilizzerai spesso, potresti considerare di trasformarlo in una vista.
Quindi analizziamolo un po'. Il nucleo è questa selezione interna, che utilizza lag()
per ottenere il valore precedente della riga, dal record di controllo precedente per quell'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
Questo ci fornisce una vista temporanea che ha tutte le colonne delle tabelle di controllo più la colonna del ritardo che viene quindi utilizzata per unpivot()
operazione, che puoi utilizzare poiché hai contrassegnato la domanda come 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')
)
Ora abbiamo una vista temporanea che ha id, action, when, col, value, prev_value
colonne; in questo caso, poiché ho solo tre colonne, che ha tre volte il numero di righe nella tabella di controllo. Infine i filtri di selezione esterni che visualizzano per includere solo le righe in cui il valore è cambiato, ovvero dove value != prev_value
(consentendo i valori nulli).
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)
Sto usando case
per stampare semplicemente qualcosa, ma ovviamente puoi fare quello che vuoi con i dati. Il distinct
è necessario perché insert
anche le voci nella tabella di controllo vengono convertite in tre righe nella vista non pivot e sto mostrando lo stesso testo per tutti e tre dal mio primo case
clausola.