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, прежде чем вставлять ее в текстовый blob.
есть ли возможность сделать это, не используя GTTs?
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 ; ^
этот инструмент является решением firebirds для вашей проблемы:
http://www.upscene.com/products.audit.iblm_main.php
в противном случае вы не можете получить доступ к новому./старый. переменные динамически.
Я исследовал решение на основе инструкции execute, но это также тупик.
использование инструкции EXECUTE с переменной контекста (новой или старой) будет никогда не работает, потому что это доступно только внутри триггера, а не в новом оператор (оператор EXECUTE) не выполняется внутри триггера, хотя он использует одно и то же соединение и транзакцию.