sql >> Databáze >  >> RDS >> Oracle

auditování 50 sloupců pomocí oracle trigger

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.



  1. Server WEBrick rails se nespustí

  2. 12c Změny DBA_USERS

  3. Existuje nějaký legitimní důvod pro použití soketů Unix přes TCP/IP s mysql?

  4. Jak otestovat, zda je SimpleXML nainstalován na mém PHP nebo ne?