Váš bezprostřední problém s else
je vždy voláno proto, že používáte proměnnou indexu r
přímo, namísto hledání názvu příslušného sloupce:
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;
Zobrazujete také pouze id
při vytváření tabulky, takže když r
je 2
, vždy to řekne, že vkládá name
, nikdy se neaktualizuje. Ještě důležitější je, pokud jste měli name
a aktualizovali to pouze pro dané id
, tento kód zobrazí id
jako vkládání, když se nezměnilo. Musíte rozdělit vložení/aktualizaci do samostatných bloků:
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;
To bude stále říkat, že se vkládá name
i když sloupec neexistuje, ale předpokládám, že je to chyba a předpokládám, že byste se snažili naplnit seznam jmen z user_tab_columns
každopádně pokud se opravdu chcete pokusit, aby to bylo dynamické.
Souhlasím s (alespoň některými) s ostatními, že by vám pravděpodobně lépe vyhovovala tabulka auditu, která kopíruje celý řádek, nikoli jednotlivé sloupce. Vaše námitka se zdá být komplikací individuálního vypisování, které sloupce se změnily. Tyto informace byste stále mohli získat s trochou práce zrušením pivotování tabulky auditu, když potřebujete data po sloupcích. Například:
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
Máte tedy jeden řádek auditu pro každou provedenou akci, dvě vložení a tři aktualizace. Chcete však vidět samostatná data pro každý sloupec, který se změnil.
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
Pět záznamů auditu se změnilo na sedm aktualizací; tři aktualizační příkazy ukazují pět upravených sloupců. Pokud to budete často používat, můžete zvážit, že to uděláte jako zobrazení.
Pojďme to tedy trochu rozebrat. Jádrem je tento vnitřní výběr, který používá lag()
získat předchozí hodnotu řádku z předchozího záznamu auditu pro dané 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
To nám poskytuje dočasný pohled, který obsahuje všechny sloupce auditních tabulek plus sloupec zpoždění, který se pak používá pro unpivot()
operaci, kterou můžete použít, protože jste otázku označili jako 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')
)
Nyní máme dočasný pohled, který obsahuje id, action, when, col, value, prev_value
sloupy; v tomto případě mám pouze tři sloupce, což má trojnásobný počet řádků v auditní tabulce. Vnější výběr nakonec filtruje, aby zahrnoval pouze řádky, kde se hodnota změnila, tj. kde value != prev_value
(povoluje hodnoty 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)
Používám case
jen něco vytisknout, ale s daty si samozřejmě můžete dělat, co chcete. distinct
je potřeba, protože insert
položky v tabulce auditu jsou také převedeny na tři řádky v neotočném zobrazení a pro všechny tři uvádím stejný text z mého prvního case
doložka.