如何在plpgsql中更新与插入/更新相同的行而未达到max_stack_depth

时间:2019-04-09 09:35:46

标签: postgresql plpgsql database-trigger

我的问题是我达到了堆栈极限。消息错误显示“您应该增加max_stack_depth”,并向我显示用于更新另一列的行。

更新请求后,我遇到此错误(下面的代码)。

我知道我的问题可能看起来像其他问题,但是没有一个可以解释为什么我会遇到此错误。

我想做的很简单,我做了很多次,但是在这里我缺少了一些东西。

我想要:如果表support_fh上有更新,请扣动触发器。我希望此触发器能够做到:

如果更新请求的新值为section = 'DISTRIBUTION'modulo = '6'fabricant = 'NEXANS'和{{ 1}} = capacite,然后设置12 = diametre(下面的代码)。

当然,这是'12.5'所在的行,与更新请求所在的行相同。

还有一点,我知道我应该使用diametre类型而不是character varying类型,但是我被要求这样做。

我的触发功能:

integer

我的触发器:

create or replace function maj_diam() returns trigger
as
$$
Declare fab_loc character varying;
Declare section_loc character varying;
Declare capa_loc character varying;
Declare modulo_loc character varying;

BEGIN
    Select fabricant into fab_loc from support_fh where id = new.id;
    Select section into section_loc from support_fh where id = new.id;
    Select capcite into capa_loc from support_fh where id = new.id;
    Select modulo into modulo_loc from support_fh where id = new.id;

    if fab_loc = 'NEXANS' and section_loc = 'DISTRIBUTION'
       and capa_loc = '12' and modulo_loc = '6' then
        update support_fh set diametre = '12.2' where id = new.id;
    endif;

    return new;
end;
$$;

我的更新请求以测试我的触发器:

create trigger maj_diam
After update on support_fh
for each row
execute procedure maj_diam();

我想从中学到东西,所以,如有可能,请向我解释我在这里做错了什么,或者我的方法缺乏见识。

2 个答案:

答案 0 :(得分:2)

您会遇到此问题,因为触发器中的更新将再次启动触发器,从而导致无限循环。 max_stack_depth的值没有足够大的值(并且过分增大该值仍然很危险)。

您应该创建一个BEFORE触发器,并修改即将插入的NEW值,而不是您正在做什么:

IF NEW.fab_loc = 'NEXANS' AND NEW.section_loc = 'DISTRIBUTION'
   AND NEW.capa_loc = '12' AND NEW.modulo_loc = '6'
THEN
   NEW.diametre := '12.2';
END IF;

答案 1 :(得分:1)

如果要更改已更新(或插入)的行中的列,请不要在触发函数中使用UPDATE。将触发器声明为BEFORE UPDATE,然后简单地分配新值。

您也不需要四个select语句从同一张表中读取四列。

但是,由于您只访问更新过的同一行中的列,因此甚至根本不需要SELECT。

因此您的触发功能可以简化为:

create or replace function maj_diam() returns trigger
as
$$
BEGIN
   if new.fabricant = 'NEXANS' 
      and new.section = 'DISTRIBUTION' 
      and new.capcite = '12' 
      and new.modulo = '6' 
   then
     new.diametre := '12.2';
   end if;
   return new;
end;
$$;

假设capcite,modulo和diametre实际上是数字,则不应将它们与varchar值进行比较。因此,上面的代码可能应该是:new.diametre := 12.2;new.capcite = 12

并且触发器定义需要更改为:

create trigger maj_diam
BEFORE update on support_fh
for each row
execute procedure maj_diam();