2013-10-03 2 views
10

Je dois obtenir toutes les valeurs qui ont changé dans une rangée et afficher les modifications sur d'autres tables 'audit'. Puis-je accomplir ceci, sans écrire les conditions pour chaque élément de la rangée? Je sais que le SQL à partir http://www.firebirdfaq.org/faq133/ qui vous donne toutes les conditions de vérifications:Firebird - Récupère tous les champs modifiés dans un trigger

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

mais cela doit être écrit dans le déclencheur. Donc, si je change une table, j'ai besoin de modifier le déclencheur. Étant donné que FireBird ne permet pas d'augmenter dynamiquement la taille d'une variable varchar, je pensais lancer et concaténer toutes les valeurs dans une grande variable varchar, avant de l'insérer dans un blob de texte.

Y at-il une possibilité d'accomplir ceci, sans utiliser GTTs?

Répondre

4

Vous avez besoin d'une méta-programmation, mais avec des triggers sur les tables système, cela ne pose aucun problème.

Cette solution semble fonctionner, même si vous avez beaucoup de colonnes.

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 pour l'inventivité ... mais ça marche? [Ce thread de liste de diffusion] (http://groups.yahoo.com/neo/groups/firebird-support/conversations/topics/100294) suggère que trig les déclencheurs de la table système disparaissent quand une table est droppée, et ② cette approche à moins utilisé pour être crashy. – pilcrow

4

Cet outil est la solution firebirds pour votre problème:

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

Sinon Vous ne pouvez pas accéder au new./old. variables dynamiquement. J'ai étudié une solution basée sur une instruction d'exécution, mais c'est aussi une impasse.

Utilisation de EXECUTE STATEMENT avec une variable de contexte (neuf ou ancien) sera ne marchera jamais, parce que c'est uniquement disponible à l'intérieur d'un déclencheur, pas dans une nouvelle déclaration (l'instruction execute) n'est pas exécutée à l'intérieur de la gâchette, bien qu'il utilise la même connexion et la même transaction.

+0

Je ne peux pas utiliser un autre outil. Il doit être fait «en interne». Je vous remercie. – RBA

+1

Dans ce cas, je pense que la meilleure façon de créer quelques procédures stockées pour régénérer les déclencheurs, et les programmer pour fonctionner sur une base quotidienne (ou lorsque vous exécutez DDL-s sur votre base de données). –

Questions connexes