Oracle
 sql >> база данни >  >> RDS >> Oracle

одитиране на 50 колони с помощта на задействане на oracle

Вашият непосредствен проблем с 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 клауза.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Oracle - Защо водещата нула на число изчезва при преобразуването му в TO_CHAR

  2. Oracle получава стойност на контролната сума за блок от данни, дефиниран от клауза за избор

  3. Как да изберете свързана група елементи в Oracle SQL

  4. Преобразуването на SQL дата води до невалиден параметър на модела на числов формат.

  5. Извличане на oracle XMLType, съхранен като двоичен XML от набор от резултати в Java