简体   繁体   English

Firebird - 获取触发器内的所有修改字段

[英]Firebird - get all modified fields inside a trigger

I need to get all the values which changed in a row and post modifications on other 'audit' table. 我需要获取连续更改的所有值,并在其他“审计”表上发布修改。 Can I accomplish this, without writing the conditions for each element from the row? 我是否可以完成此操作,而无需为行中的每个元素编写条件? I know the SQL from http://www.firebirdfaq.org/faq133/ which gives you all the conditions for verifications: 我知道来自http://www.firebirdfaq.org/faq133/的SQL,它为您提供了验证的所有条件:

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';

but this should be written in the trigger. 但这应该写在触发器中。 So, if I change a table then I need to modify the trigger. 所以,如果我改变一个表,那么我需要修改触发器。

Due the fact that FireBird does not allow dynamically increasing the size of a varchar variable I was thinking of casting and concatenating all the values to a big varchar variable, before inserting it in a text blob. 由于FireBird不允许动态增加varchar变量的大小,因此在将其插入文本blob之前,我考虑将所有值转换并连接到一个大的varchar变量。

Is there any possibility to accomplish this, without using GTTs ? 有没有可能在不使用GTT的情况下实现这一目标

You need some meta programming, but with triggers on system tables that's no problem. 您需要一些元编程,但在系统表上使用触发器没有问题。

This solution seems to work, even if you have lots of columns. 即使您有很多列,此解决方案似乎也可以使用。

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 ; ^

This tool is the firebirds solution for your problem: 此工具是针对您的问题的firebirds解决方案:

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

Otherwise You can't access the new./old. 否则您无法访问new./old。 variables dynamically. 变量动态。

I investigated an execute statement based solution, but it is also a dead-end. 我调查了基于执行语句的解决方案,但它也是一个死胡同。

Using EXECUTE STATEMENT with a context variable (NEW or OLD) will never work, cause that's only available inside a trigger, not in a new statement (the EXECUTE STATEMENT) is not executed inside the trigger, although it uses the same connection and transaction. 将EXECUTE STATEMENT与上下文变量(NEW或OLD)一起使用将永远不会起作用,因为它只在触发器内可用,而不是在新语句中(EXECUTE STATEMENT)不在触发器内执行,尽管它使用相同的连接和事务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM