Oracle
 sql >> Baza danych >  >> RDS >> Oracle

audyt 50 kolumn za pomocą wyzwalacza Oracle

Twój bezpośredni problem z else zawsze wywoływane jest to, że używasz zmiennej indeksu r bezpośrednio, zamiast wyszukiwania odpowiedniej nazwy kolumny:

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;

Pokazujesz też tylko id kolumna w tworzeniu tabeli, więc kiedy r to 2 , zawsze powie, że wstawia name , nigdy się nie aktualizuje. Co ważniejsze, jeśli miałeś name kolumny i aktualizowały tylko to dla danego id , ten kod pokaże id jak wstawianie, gdy się nie zmieniło. Musisz podzielić wstawkę/aktualizację na osobne bloki:

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 nadal będzie oznaczać, że wstawia name nawet jeśli kolumna nie istnieje, ale zakładam, że to pomyłka i przypuszczam, że próbowałbyś wypełnić listę nazw z user_tab_columns w każdym razie, jeśli naprawdę chcesz spróbować uczynić to dynamicznym.

Zgadzam się z (przynajmniej niektórymi) innymi, że prawdopodobnie lepiej byłoby, gdyby tabela kontroli zawierała kopię całego wiersza, a nie pojedynczych kolumn. Twój sprzeciw wydaje się być komplikacją indywidualnego wymienienia, które kolumny uległy zmianie. Możesz nadal uzyskać te informacje, przy odrobinie pracy, cofając przestawną tabelę inspekcji, gdy potrzebujesz danych kolumna po kolumnie. Na przykład:

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

Masz więc jeden wiersz kontroli dla każdego podjętego działania, dwie wstawki i trzy aktualizacje. Ale chcesz zobaczyć osobne dane dla każdej zmienionej kolumny.

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

Pięć rekordów audytu przekształciło się w siedem aktualizacji; trzy instrukcje aktualizacji pokazują pięć zmodyfikowanych kolumn. Jeśli będziesz tego często używać, możesz rozważyć dodanie tego do widoku.

Więc rozłóżmy to trochę. Rdzeniem jest ta wewnętrzna selekcja, która używa lag() aby uzyskać poprzednią wartość wiersza z poprzedniego rekordu kontroli dla tego 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

Daje nam to tymczasowy widok, który zawiera wszystkie kolumny tabel kontroli oraz kolumnę z opóźnieniem, która jest następnie używana przez unpivot() operacja, której możesz użyć, ponieważ oznaczyłeś pytanie 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')
    )

Teraz mamy tymczasowy widok, który ma id, action, when, col, value, prev_value kolumny; w tym przypadku, ponieważ mam tylko trzy kolumny, czyli trzy razy więcej wierszy w tabeli audytu. Wreszcie zewnętrzne filtry wyboru, które wyświetlają tylko wiersze, w których zmieniła się wartość, tj. gdzie value != prev_value (pozwala na wartości 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)

Używam case po prostu coś wydrukować, ale oczywiście możesz zrobić z danymi, co chcesz. distinct jest potrzebne, ponieważ insert wpisy w tabeli audytu są również konwertowane na trzy wiersze w widoku nieprzestawnym i pokazuję ten sam tekst dla wszystkich trzech z mojego pierwszego case klauzula.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. łącząca wyrocznię z r

  2. Jak agregować bez użycia `GROUP BY`?

  3. Oracle PL/SQL — zgłaszanie wyjątków zdefiniowanych przez użytkownika za pomocą niestandardowego SQLERRM

  4. Dlaczego Oracle SQL w tajemniczy sposób rozwiązuje niejednoznaczność w jednym sprzężeniu, a nie w innych?

  5. Jak używać indeksu opartego na funkcjach w kolumnie zawierającej wartości NULL w Oracle 10+?