sql >> Database teknologi >  >> RDS >> Oracle

revision af 50 kolonner ved hjælp af oracle trigger

Dit umiddelbare problem med else altid at blive kaldt er fordi du bruger din indeksvariabel r direkte i stedet for at slå det relevante kolonnenavn op:

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;

Du viser også kun et id kolonne i din tabeloprettelse, så når r er 2 , vil den altid sige, at den indsætter navn , opdaterer aldrig. Endnu vigtigere, hvis du havde et navn kolonne og opdaterede kun det for et givet id , ville denne kode vise id som at indsætte, når det ikke havde ændret sig. Du skal opdele indsættelsen/opdateringen i separate blokke:

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;

Dette vil stadig sige, at det indsætter navn selvom kolonnen ikke eksisterer, men jeg antager, at det er en fejl, og jeg gætter på, at du ville prøve at udfylde listen over navne fra user_tab_columns alligevel, hvis du virkelig vil prøve at gøre det dynamisk.

Jeg er enig med (i hvert fald nogle af) de andre i, at du sandsynligvis ville være bedre stillet med en revisionstabel, der tager en kopi af hele rækken, frem for individuelle kolonner. Din indsigelse ser ud til at være komplikationen ved individuelt at angive, hvilke kolonner der er ændret. Du kan stadig få disse oplysninger, med lidt arbejde, ved at deaktivere revisionstabellen, når du har brug for kolonne-for-kolonne data. For eksempel:

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

Så du har én revisionsrække for hver udført handling, to indsættelser og tre opdateringer. Men du ønsker at se separate data for hver kolonne, der ændrede sig.

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

De fem revisionsprotokoller er blevet til syv opdateringer; de tre opdateringssætninger viser de fem modificerede kolonner. Hvis du kommer til at bruge dette meget, kan du overveje at gøre det til en visning.

Så lad os bryde det lidt ned. Kernen er dette indre udvalg, som bruger lag() for at hente den forrige værdi af rækken fra den forrige revisionspost for det 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

Det giver os en midlertidig visning, som har alle revisionstabellernes kolonner plus lagkolonnen, som derefter bruges til unpivot() operation, som du kan bruge, da du har tagget spørgsmålet som 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')
    )

Nu har vi en midlertidig visning, som har id, action, when, col, value, prev_value søjler; i dette tilfælde, da jeg kun har tre kolonner, har det tre gange antallet af rækker i revisionstabellen. Til sidst filtrerer det ydre udvalg denne visning til kun at inkludere de rækker, hvor værdien er ændret, dvs. hvor værdi !=prev_value (der tages højde for nuller).

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)

Jeg bruger case at bare printe noget, men du kan selvfølgelig gøre hvad du vil med dataene. Den distinkte er nødvendig, fordi insert poster i revisionstabellen konverteres også til tre rækker i den ikke-pivoterede visning, og jeg viser den samme tekst for alle tre fra min første sag klausul.



  1. Hvordan ændrer jeg en PostgreSQL-tabel og gør en kolonne unik?

  2. En bedre måde at udføre flere MySQL-kommandoer ved hjælp af shell-script

  3. Hibernate opretter ikke jointabel

  4. Hvorfor blev forespørgslen ikke opdateret efter klik på opdateringsknappen?