Triggers 如何使用update触发器获取主表中明细表字段的总和

Triggers 如何使用update触发器获取主表中明细表字段的总和,triggers,firebird,firebird-3.0,firebird-psql,Triggers,Firebird,Firebird 3.0,Firebird Psql,Firebird 3.0数据库中有主表“factgad”和明细表“recgad” factgad:factgad_k(pr_k) recgad:recgad_k(pr_k),factgad_k(fk) 当我更新主表时,我必须得到详细表记录的总和,但我无法编写正确的代码。当我尝试更新主表时,出现错误: Error Message: ---------------------------------------- Too many concurrent executions of the same

Firebird 3.0数据库中有主表“factgad”和明细表“recgad”

factgad:factgad_k(pr_k)

recgad:recgad_k(pr_k),factgad_k(fk)

当我更新主表时,我必须得到详细表记录的总和,但我无法编写正确的代码。当我尝试更新主表时,出现错误:

Error Message:
----------------------------------------
Too many concurrent executions of the same request.
Too many concurrent executions of the same request.
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, col: 3
At trigger 'FACTGAD_AU' line: 21, co...
---------------------------------------------------
SQLCODE: -693
SQLSTATE: 54001
GDSCODE: 335544663



create or alter trigger factgad_AU FOR factgad
ACTIVE AFTER UPDATE POSITION 0
AS
DECLARE VARIABLE newl DECIMAL(8, 4);
DECLARE VARIABLE oldl DECIMAL(8, 4);
DECLARE VARIABLE newd DECIMAL(8, 4);
DECLARE VARIABLE oldd DECIMAL(8, 4);
DECLARE VARIABLE FACTGAD_K INTEGER;
BEGIN
 select
        f.factgad_k,
        sum(r.fnewl*r.rgad),
        sum(r.foldl*r.rgad),
        sum(r.fnewd*r.rgad),
        sum(r.foldd*r.rgad)
   from recgad r, factgad f
   where f.factgad_k=new.factgad_k and r.factgad_k=f.factgad_k
   group by f.factgad_k
   into  :factgad_k,:NEWL, :OLDL, :NEWD, :OLDD;

  update factgad set
    factgad.NEWL=:NEWL,
    factgad.OLDL=:OLDL,
    factgad.NEWD=:NEWD,
    factgad.OLDD=:OLDD
   where factgad_k=:FACTGAD_K;
end
触发器SQL有什么问题?我尝试在where子句
中使用
对f.factgad_k=new.factgad_k
进行更改,其中f.factgad_k=43
,但出现了相同的错误。重新启动Firebird服务未发生任何更改

奇怪的行为,同样的错误出现在更新后触发器中,该触发器在更新详细信息表后使用详细信息表的总和更新主表:

CREATE OR ALTER TRIGGER RECGAD_AU FOR RECGAD
ACTIVE AFTER UPDATE POSITION 0
AS
DECLARE VARIABLE NEWL DECIMAL(8, 4);
DECLARE VARIABLE OLDL DECIMAL(8, 4);
DECLARE VARIABLE NEWD DECIMAL(8, 4);
DECLARE VARIABLE OLDD DECIMAL(8, 4);
DECLARE VARIABLE FACTGAD_K INTEGER;
begin
 select
        r.factgad_k,
        sum(r.fnewl*r.rgad),
        sum(r.foldl*r.rgad),
        sum(r.fnewd*r.rgad),
        sum(r.foldd*r.rgad)
   from recgad r, factgad f
   where r.factgad_k=new.factgad_k and r.factgad_k=f.factgad_k
   group by r.factgad_k
   into  :factgad_k,:NEWL, :OLDL, :NEWD, :OLDD;

  update factgad set
    factgad.NEWL=:NEWL,
    factgad.OLDL=:OLDL,
    factgad.NEWD=:NEWD,
    factgad.OLDD=:OLDD
   where factgad_k=:FACTGAD_K;
end 

您正试图在一个触发器中更新表
FACTGAD
,该触发器在更新表
FACTGAD
时触发。换言之,更新触发触发器,更新触发触发器,触发触发器等等。这将最终触发错误“同一请求的并发执行过多”

您不应该在对同一个表进行更新时触发的触发器中使用
UPDATE
。相反,您应该在更新之前使用
触发器,并将更新后的值分配给
新的
上下文的列。在
INSERT
UPDATE
上的
BEFORE UPDATE
触发器中,修改
NEW
上下文将更新要插入或更新的行。但是,在主表上触发的触发器中重新计算值表中的值之和没有多大意义:考虑主表是否从未更新,但详细行被添加、删除或更新会发生什么情况。 至于你的第二个触发器,我只能猜测你的触发器仍然在
FACTGAD
上,或者你有其他触发器,导致
FACTGAD
RECGAD
之间的更新周期

另一方面,您执行的选择不需要从
FACTGAD
中进行选择,假设您在两者之间有一个外键约束


<> TLDR:删除触发器<代码> FasAdgAuAu/COD>,保留触发器<代码> ReGADAGUA(但考虑在插入或更新< /代码>之后使其成为<代码>。

< P>一般不可能得到主表中的详细记录的可靠。最好的方法是通过细节表上的触发器添加和减去数据,但在加载时,它最终会导致更新冲突。在并发环境中,触发器将以完全错误的值结束


通常,select中的聚合计算性能很好。

请不要发布文本信息的屏幕截图,而是以代码格式的文本发布错误消息。这提高了可读性和可发现性。@MarkRotterVeel您的意思是删除错误消息屏幕并仅发布此错误消息的sqlcode和sqlstate?否,将错误消息发布为文本,而不是屏幕截图(使用“复制”按钮)。在更新收到相同错误之前,请标记RotterVeel。据我所知,用update触发器更新同一个表是不可能的。Mark Rotterveel,你说:“你执行的选择不需要从FACTGAD中选择,假设你在两者之间有一个外键约束。”。是的,我有外部约束,但您的意思是在触发器中的delete select语句,在“update factgad set…”之前?如果我删除select语句,那么如何初始化变量?我忘了说我删除了触发器factgad_AU,RECGAD_AU工作正常。:)谢谢,@basti关于你的第一个评论,我的建议是在更新之前使用
,并通过
新的
上下文进行更新,而不是直接更新表。至于你的第二条评论:我没有说你不应该选择,我只是说你只需要从
recgad
中选择,你对
factgad
的连接是不必要的。@basti在表
factgad
上触发的触发器中,你不应该使用
更新factgad
,因为那样会一次又一次地触发触发器,一次又一次。相反,使用赋值语句(例如,
NEW.OLDD=:OLDD;
等)将值赋值给
NEW
上下文的列。讨论了在多级表中保存聚合的非常明智的策略(例如,旧聚合-年度、最近两年每月等)。但也许只有在非常大的数据上才值得这么做。就我个人而言,我最终得到了“lazy eval”解决方案:1)修改details表删除数据,即将master的列设置为
NULL
,2)查询检查是否存在NULL以及是否有运行计算SP的情况。授权的情况下,读取的频率远低于写入的频率,而且行重叠的可能性更小。最后,在
选择之前,我并不真正需要中间数据。只需“刷新缓存”