Ваша непосредственная проблема с 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
Таким образом, у вас есть одна строка аудита для каждого предпринятого действия, две вставки и три обновления. Но вы хотите увидеть отдельные данные для каждого измененного столбца.
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
Пять записей аудита превратились в семь обновлений; три оператора обновления показывают пять измененных столбцов. Если вы будете использовать это много, вы можете подумать о том, чтобы сделать это в представлении.
Так что давайте немного разобьем это. Ядро это внутренний выбор, который использует 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
колонны; в этом случае, поскольку у меня есть только три столбца, которые в три раза превышают количество строк в таблице аудита. Наконец, внешние фильтры выбора, которые позволяют включать только строки, в которых значение изменилось, то есть где value != prev_value
(с учетом нулей).
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
в таблице аудита также преобразуются в три строки в неизолированном виде, и я показываю один и тот же текст для всех трех из моего первого предложения case
.
спасибо за хорошее объяснение, я никогда не думаю о том, что это можно сделать с помощью univot, но вопрос в том, что это оптимизируется, потому что есть миллионы транзакций, совершаемых за один день, если я вызову этот запрос, тогда это будет оптимизировано? за исключением использования простого if-else в триггере –