sql >> Databasteknik >  >> RDS >> Oracle

granska 50 kolumner med oracle trigger

Ditt omedelbara problem med else att alltid anropas beror på att du använder din indexvariabel r direkt, istället för att leta upp det relevanta kolumnnamnet:

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 visar också bara ett id kolumnen i din tabellskapande, så när r är 2 , kommer det alltid att stå att det infogar namn , uppdaterar aldrig. Ännu viktigare, om du hade ett namn kolumnen och uppdaterade bara det för ett givet id , skulle den här koden visa id som att sätta in när det inte hade ändrats. Du måste dela upp infogningen/uppdateringen i separata block:

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;

Detta kommer fortfarande att säga att det infogar namn även om kolumnen inte finns, men jag antar att det är ett misstag, och jag antar att du skulle försöka fylla i listan med namn från user_tab_columns i alla fall om du verkligen vill försöka göra det dynamiskt.

Jag håller med (åtminstone några av) de andra att du förmodligen skulle vara bättre med en granskningstabell som tar en kopia av hela raden, snarare än enskilda kolumner. Din invändning verkar vara komplikationen av att individuellt lista vilka kolumner som ändrats. Du kan fortfarande få den här informationen, med lite arbete, genom att avpivotera granskningstabellen när du behöver kolumn-för-kolumn-data. Till exempel:

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 en granskningsrad för varje vidtagen åtgärd, två inlägg och tre uppdateringar. Men du vill se separata data för varje kolumn som ändrats.

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 revisionsprotokollen har förvandlats till sju uppdateringar; de tre uppdateringssatserna visar de fem modifierade kolumnerna. Om du kommer att använda det här mycket kan du överväga att göra det till en vy.

Så låt oss bryta ner det lite. Kärnan är detta inre urval, som använder lag() för att hämta det tidigare värdet på raden, från den tidigare granskningsposten för 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 ger oss en tillfällig vy som har alla granskningstabellernas kolumner plus fördröjningskolumnen som sedan används för unpivot() operation, som du kan använda eftersom du har taggat frågan 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 tillfällig vy som har id, action, when, col, value, prev_value kolumner; i det här fallet eftersom jag bara har tre kolumner, det har tre gånger antalet rader i granskningstabellen. Slutligen filtrerar det yttre urvalet den vyn så att den endast inkluderar raderna där värdet har ändrats, dvs. där värde !=prev_value (medger nollor).

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)

Jag använder case att bara skriva ut något, men självklart kan du göra vad du vill med datan. Den distinkt behövs eftersom insert poster i granskningstabellen konverteras också till tre rader i den opivoterade vyn, och jag visar samma text för alla tre från mitt första case klausul.



  1. PostgreSQL kan inte påbörja/avsluta transaktioner i PL/pgSQL

  2. Kan jag automatisera att infoga lösenordet för PEM-filen för SSL-certifikat i MySQL?

  3. Skickar +-200 e-postmeddelanden med funktionen php mail() i en loop

  4. Fråga efter data genom att sammanfoga två tabeller i två databaser på olika servrar