Votre problème immédiat avec le else
toujours appelé, c'est parce que vous utilisez votre variable d'index r
directement, plutôt que de rechercher le nom de la colonne 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;
Vous ne montrez également qu'un id
colonne dans la création de votre table, donc quand r
est 2
, il dira toujours qu'il insère name
, jamais mis à jour. Plus important encore, si vous aviez un name
colonne et ne mettaient à jour que pour un id
donné , ce code afficherait le id
comme insertion alors qu'il n'avait pas changé. Vous devez diviser l'insertion/la mise à jour en blocs distincts :
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;
Cela dira toujours qu'il insère name
même si la colonne n'existe pas, mais je suppose que c'est une erreur, et je suppose que vous essayez de remplir la liste des noms de user_tab_columns
de toute façon si vous voulez vraiment essayer de le rendre dynamique.
Je suis d'accord avec (au moins certains des) autres que vous seriez probablement mieux avec une table d'audit qui prend une copie de la ligne entière, plutôt que des colonnes individuelles. Votre objection semble être la complication de la liste individuelle des colonnes modifiées. Vous pouvez toujours obtenir ces informations, avec un peu de travail, en désactivant la table d'audit lorsque vous avez besoin de données colonne par colonne. Par exemple :
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
Vous avez donc une ligne d'audit pour chaque action entreprise, deux insertions et trois mises à jour. Mais vous voulez voir des données distinctes pour chaque colonne qui a changé.
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
Les cinq enregistrements d'audit se sont transformés en sept mises à jour ; les trois instructions de mise à jour montrent les cinq colonnes modifiées. Si vous l'utilisez souvent, vous pouvez envisager d'en faire une vue.
Alors, décomposons cela un peu. Le noyau est cette sélection interne, qui utilise lag()
pour obtenir la valeur précédente de la ligne, à partir de l'enregistrement d'audit précédent pour cet 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
Cela nous donne une vue temporaire qui contient toutes les colonnes des tables d'audit plus la colonne de décalage qui est ensuite utilisée pour le unpivot()
opération, que vous pouvez utiliser car vous avez marqué la question comme 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')
)
Nous avons maintenant une vue temporaire qui a id, action, when, col, value, prev_value
Colonnes; dans ce cas, comme je n'ai que trois colonnes, cela a trois fois le nombre de lignes dans la table d'audit. Enfin, les filtres de sélection externes qui s'affichent pour n'inclure que les lignes où la valeur a changé, c'est-à-dire où value != prev_value
(autorisant les valeurs nulles).
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)
J'utilise case
simplement imprimer quelque chose, mais bien sûr, vous pouvez faire ce que vous voulez avec les données. Le distinct
est nécessaire car le insert
les entrées de la table d'audit sont également converties en trois lignes dans la vue non pivotée, et j'affiche le même texte pour les trois depuis mon premier case
clause.