【问题标题】:Firebird - get all modified fields inside a triggerFirebird - 获取触发器内的所有修改字段
【发布时间】:2013-10-10 01:03:45
【问题描述】:

我需要获取所有连续更改的值并在其他“审计”表上发布修改。我可以在不为行中的每个元素编写条件的情况下完成此操作吗?我知道来自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';

但这应该写在触发器中。所以,如果我改变一个表,那么我需要修改触发器。

由于 FireBird 不允许动态增加 varchar 变量的大小,我正在考虑将所有值转换并连接到一个大的 varchar 变量中,然后再将其插入文本 blob。

有没有可能在不使用GTTs 的情况下做到这一点?

【问题讨论】:

    标签: sql triggers firebird firebird2.5 audit-trail


    【解决方案1】:

    此工具是解决您问题的 firebirds 解决方案:

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

    否则你无法访问新的./old。动态变量。

    我研究了一个基于执行语句的解决方案,但它也是一个死胡同。

    使用带有上下文变量(NEW 或 OLD)的 EXECUTE STATEMENT 将 永远不会工作,因为它只能在触发器中使用,而不是在新的 语句(EXECUTE STATEMENT)不在触发器内部执行, 尽管它使用相同的连接和事务。

    【讨论】:

    • 我无法使用其他工具。它需要“内部”制作。谢谢。
    • 在这种情况下,我认为最好的方法是创建一些存储过程来重新生成触发器,并安排它们每天运行(或者当您在数据库上运行 DDL-s 时)。
    【解决方案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 ; ^
    

    【讨论】:

    • +1 表示创造力……但它有效吗? This mailing list thread 建议①当某些表被 DROPped 时系统表触发器消失,②这种方法至少曾经是崩溃的。
    猜你喜欢
    • 2010-11-18
    • 1970-01-01
    • 2019-07-06
    • 2011-07-28
    • 1970-01-01
    • 1970-01-01
    • 2020-03-23
    • 2016-01-01
    • 1970-01-01
    相关资源
    最近更新 更多