2013-10-03 29 views
10

我需要获取连续更改的所有值,并在其他“审计”表上修改后的值。我能否实现这一点,而不必为行中的每个元素写入条件?我知道,从http://www.firebirdfaq.org/faq133/,让你为验证所有条件SQL:Firebird - 获取触发器内的所有修改字段

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变量的大小,因此在将它插入文本blob之前,我正在考虑将所有值转换并连接到大型varchar变量。

有没有可能做到这一点,而不使用GTTs

回答

4

您需要一些元编程,但系统表上的触发器没有问题。

即使您有很多列,此解决方案似乎也能正常工作。

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

为了创造力+1,但它有效吗? [这个邮件列表线程](http://groups.yahoo.com/neo/groups/firebird-support/conversations/topics/100294)表明①当某个表被删除时,系统表触发器消失,②这种方法在最不习惯的是失败。 – pilcrow

4

这个工具是你的问题的解决火鸟:

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

否则您将无法访问new./old。动态变量。

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

使用EXECUTE语句的上下文变量(新的或旧)将 从来没有工作,导致这只是提供一个触发器中,而不是在一个新的 声明(执行语句)不会触发内部执行, 尽管它使用相同的连接和事务。

+0

我不能使用其他工具。它需要在内部进行。谢谢。 – RBA

+1

在这种情况下,我认为创建几个存储过程来重新生成触发器,并安排它们在每日基础上运行(或者当您在数据库上运行DDL-s时)的最佳方法。 –

相关问题