2013-10-03 56 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時)的最佳方法。 –

相關問題