[英]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.