Firebird - вземете всички модифицирани полета вътре в тригер

Трябва да получа всички стойности, които са се променили в ред и да публикувам модификации в друга таблица за „одит“. Мога ли да постигна това, без да пиша условията за всеки елемент от реда? Познавам SQL от http://www.firebirdfaq.org/faq133/, който ви дава всичко условията за проверка:

select 'if (new.' || rdb$field_name || ' is null and old.' ||
rdb$field_name || ' is not null or new.' || rdb$field_name ||
'is not null and old.' || rdb$field_name || ' is null or new.' ||
rdb$field_name || ' <> old.' || rdb$field_name || ') then'
from rdb$relation_fields
where rdb$relation_name = 'EMPLOYEE';

но това трябва да бъде написано в тригера. Така че, ако променя таблица, трябва да променя тригера.

Поради факта, че FireBird не позволява динамично увеличаване на размера на променлива varchar, мислех да прехвърлям и свържа всички стойности към голяма променлива varchar, преди да я вмъкна в текстов блок.

Има ли някаква възможност да се постигне това, без да се използват GTTs?


person RBA    schedule 03.10.2013    source източник


Отговори (2)


Имате нужда от мета програмиране, но със задействания на системни таблици това не е проблем.

Това решение изглежда работи, дори ако имате много колони.

set term ^ ;

create or alter procedure create_audit_update_trigger (tablename char(31)) as
    declare sql blob sub_type 1;
    declare fn char(31);
    declare skip decimal(1);
begin
    -- TODO add/remove fields to/from audit table

    sql = 'create or alter trigger ' || trim(tablename) || '_audit_upd for ' || trim(tablename) || ' after update as begin if (';

    skip = 1;
    for select rdb$field_name from rdb$relation_fields where rdb$relation_name = :tablename into :fn do
    begin
        if (skip = 0) then sql = sql || ' or ';
        sql = sql || '(old.' || trim(:fn) || ' is distinct from new.' || trim(:fn) || ')';
        skip = 0;
    end
    sql = sql || ') then insert into ' || trim(tablename) || '_audit (';

    skip = 1;
    for select rdb$field_name from rdb$relation_fields where rdb$relation_name = :tablename into :fn do
    begin
        if (skip = 0) then sql = sql || ',';
        sql = sql || trim(:fn);
        skip = 0;
    end
    sql = sql || ') values (';

    skip = 1;
    for select rdb$field_name from rdb$relation_fields where rdb$relation_name = :tablename into :fn do
    begin
        if (skip = 0) then sql = sql || ',';
        sql = sql || 'new.' || trim(:fn);
        skip = 0;
    end
    sql = sql || '); end';

    execute statement :sql;
end ^

create or alter trigger field_audit for rdb$relation_fields after insert or update or delete as
begin
    -- TODO filter table name, don't include system or audit tables
    -- TODO add insert trigger
    execute procedure create_audit_update_trigger(new.rdb$relation_name);
end ^

set term ; ^
person Christoph Walesch    schedule 14.10.2013
comment
+1 за изобретателност ... но работи ли? Тази нишка от пощенския списък предполага, че ① системната таблица тригерите изчезват, когато някоя таблица бъде DROPped и ② този подход поне беше срив. - person pilcrow; 14.10.2013

Този инструмент е решението на firebirds за вашия проблем:

http://www.upscene.com/products.audit.iblm_main.php

В противен случай нямате достъп до новото/старото. променливи динамично.

Проучих решение, базирано на оператор за изпълнение, но то също е задънена улица.

Използването на EXECUTE STATEMENT с контекстна променлива (НОВА или СТАРА) никога няма да работи, защото това е налично само вътре в тригер, а не в нов оператор (EXECUTE STATEMENT) не се изпълнява вътре в тригера, въпреки че използва същата връзка и транзакция.

person Lajos Veres    schedule 10.10.2013
comment
Не мога да използвам друг инструмент. Трябва да се направи „вътрешно“. Благодаря ти. - person RBA; 11.10.2013
comment
В този случай мисля, че най-добрият начин е да създадете няколко съхранени процедури за повторно генериране на тригерите и да ги планирате да се изпълняват ежедневно (или когато изпълнявате DDL-и във вашата база данни). - person Lajos Veres; 11.10.2013