如何在不达到 max_stack_depth 的情况下更新与 plpgsql 中 insert/update 相同的行

How to update same line than insert/update in plpgsql without reaching max_stack_depth

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

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

我知道我的问题可能看起来像其他问题,但其中 none 解释了为什么我会遇到此错误。

我想做的事情很简单,我已经做了很多次了,但是这里我遗漏了一些东西。

我想:如果 table support_fh 上有更新,请触发。我希望此触发器执行以下操作:

如果更新请求的新值是 section= 'DISTRIBUTION' and modulo= '6' and fabricant = 'NEXANS' and 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();

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

update support_fh set fabricant = 'NEXANS', section = 'DISTRIBUTION', capacite = '12', modulo = '6' 
where id = 11827;

我想从中吸取教训,所以,如果可能的话,请向我解释我在这里做错了什么,或者我的方法是否缺乏洞察力。

您遇到这个问题是因为触发器中的更新会再次启动触发器,从而导致无限循环。 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;

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

您也不需要四个 select 语句来读取同一 table 中的四列。

但是由于您只访问已更新的同一行中的列,您甚至根本不需要 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();