sql >> Database >  >> RDS >> Oracle

50 kolommen controleren met Oracle-trigger

Uw directe probleem met de else altijd wordt aangeroepen is omdat u uw indexvariabele r . gebruikt rechtstreeks, in plaats van de relevante kolomnaam op te zoeken:

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;

Je toont ook alleen een id kolom in uw tabelcreatie, dus wanneer r is 2 , het zal altijd zeggen dat het name invoegt , nooit updaten. Wat nog belangrijker is, als je een name . had kolom en waren die alleen aan het updaten voor een gegeven id , zou deze code de id . tonen als invoegen wanneer het niet was veranderd. U moet het invoegen/bijwerken in afzonderlijke blokken splitsen:

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;

Dit zal nog steeds zeggen dat het name invoegt zelfs als de kolom niet bestaat, maar ik neem aan dat dit een vergissing is, en ik denk dat je zou proberen de lijst met namen uit user_tab_columns te vullen in ieder geval als je echt wilt proberen om het dynamisch te maken.

Ik ben het met (tenminste enkele van) de anderen eens dat je waarschijnlijk beter af bent met een controletabel die een kopie van de hele rij neemt in plaats van afzonderlijke kolommen. Uw bezwaar lijkt de complicatie te zijn van het afzonderlijk vermelden welke kolommen zijn gewijzigd. Je zou deze informatie nog steeds kunnen krijgen, met wat werk, door de controletabel ongedaan te maken wanneer je kolom-voor-kolom gegevens nodig hebt. Bijvoorbeeld:

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

U hebt dus één auditrij voor elke ondernomen actie, twee invoegingen en drie updates. Maar u wilt afzonderlijke gegevens zien voor elke kolom die is gewijzigd.

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 vijf auditrecords zijn veranderd in zeven updates; de drie update-statements tonen de vijf gewijzigde kolommen. Als je dit veel gaat gebruiken, kun je overwegen om er een weergave van te maken.

Dus laten we dat een beetje opsplitsen. De kern is deze innerlijke select, die gebruikmaakt van lag() om de vorige waarde van de rij te krijgen, uit het vorige auditrecord voor die 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

Dat geeft ons een tijdelijke weergave met alle kolommen van de controletabellen plus de lag-kolom die vervolgens wordt gebruikt voor de unpivot() bewerking, die u kunt gebruiken omdat u de vraag heeft getagd als 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 hebben we een tijdelijke weergave met id, action, when, col, value, prev_value kolommen; in dit geval omdat ik maar drie kolommen heb, dat heeft drie keer het aantal rijen in de controletabel. Ten slotte filteren de buitenste selectiefilters die weergave om alleen de rijen op te nemen waar de waarde is gewijzigd, d.w.z. waar value != prev_value (nulls toestaan).

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)

Ik gebruik case om zomaar iets af te drukken, maar je kunt natuurlijk met de gegevens doen wat je wilt. De distinct is nodig omdat de insert vermeldingen in de controletabel worden ook geconverteerd naar drie rijen in de niet-gedraaide weergave, en ik toon dezelfde tekst voor alle drie van mijn eerste case clausule.



  1. Een unieke database-ID verkrijgen voor SQL Server 2005 en later

  2. Hoe werkt MySQL CASE?

  3. Alle rijen uit een tabel verwijderen, behalve de nieuwste 10 rijen

  4. Welsprekende vraag van Laravel