在 PostgreSQL 中触发触发器时使用“CASE WHEN”更新查询不起作用

update query using `CASE WHEN` not working when trigger fired in PostgreSQL

我尝试对 PostgreSQL 中的更新查询使用 CASE WHEN 条件。

我正在做的查询是:

if new.bts_trx_gc = 'Terminé' then
        update stt set stt_doe_gc_bts = (case when old.bts_trx_gc <> new.bts_trx_gc then 'Demandé' else stt_doe_gc_bts end) where stt_id = new.bts_id;
        raise info 'test est un test';
    end if;

每次我在控制台中看到 raise info 时都会触发触发器。但是更新不起作用。

此查询是为了确保当用户通过 Web 界面更新字段时,更新仅在 old.bts_trx_gc <> new.bts_trx_gc 时触发。 因为即使未修改 bts_trx_gc 值也可能会发生 Web 界面,因此会发送带有 bts_trx_gc = Terminé 的查询。我只想在该字段从某项变为仅 Terminé 时触发更新。

考虑到上面的查询,我想做的是如果 IF 条件为真并且 old.bts_trx_gc <> new.bts_trx_gc 那么 bts_trx_gc = Demandé 否则保持相同的值。

这里是触发函数的方法:

create or replace function after_update_bts_trx_gc() returns trigger
    language plpgsql
as
$$
begin

    if new.bts_trx_gc = 'Terminé' then
        update stt set stt_doe_gc_bts = (case when old.bts_trx_gc <> new.bts_trx_gc then 'Demandé' else stt_doe_gc_bts end) where stt_id = new.bts_id;
        raise info 'test est un test';
    end if;

    return new;

end;
$$;

这里是触发器:

create trigger after_update_bts_trx_gc
    after update
    on etude_bts
    for each row
execute procedure after_update_bts_trx_gc();

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

我会使用 IF 有条件地 运行 UPDATE,而不是 CASE 表达式。这还有一个额外的好处,即您不会 运行 不需要的更新。

如果 bts_trx_gc 可以包含空值,您应该使用 is distinct from 而不是 <> 来正确处理 NULL 值。

create or replace function after_update_bts_trx_gc() returns trigger
    language plpgsql
as
$$
begin
  if new.bts_trx_gc = 'Terminé' and old.bts_trx_gc IS DISTINCT FROM new.bts_trx_gc then
    UPDATE stt 
       set stt_doe_gc_bts = 'Demandé'
    WHERE stt_id = new.bts_id;

    raise info 'test est un test';
  end if;
  return new;
end;
$$;

这是我最初的回答,我忽略了更新针对不同 table 的事实。不过,我将其留作参考。

不要使用更新来更改触发器中的行。使用 BEFORE UPDATE 触发器并在满足条件时将值分配给 new 行:

create or replace function after_update_bts_trx_gc() returns trigger
    language plpgsql
as
$$
begin
  if new.bts_trx_gc = 'Terminé' and old.bts_trx_gc <> new.bts_trx_gc then
    new.stt_doe_gc_bts := 'Demandé';
    raise info 'test est un test';
  end if;
  return new;
end;
$$;

但是您需要一个 BEFORE 触发器才能使其工作:

create trigger after_update_bts_trx_gc
    BEFORE update
    on etude_bts
    for each row
execute procedure after_update_bts_trx_gc();

如果列不发生变化,完全不触发触发器会更有效:

create trigger after_update_bts_trx_gc
    BEFORE update 
    on etude_bts
    for each row
    when (old.bts_trx_gc <> new.bts_trx_gc)
execute procedure after_update_bts_trx_gc();

CASE WHEN 在触发器中对我不起作用(总是 returns 在第一个 WHEN 上为真):

myTag = '[' || TG_TABLE_NAME ||
    CASE WHEN TG_TABLE_NAME = 'scac_proformat'
        THEN
        CASE WHEN tg_op IN ('UPDATE','INSERT')
            THEN coalesce(NEW.format_type,'P')
            ELSE coalesce(OLD.format_type,'P')
        END
    ELSE ''
    END
    || ']';

这样就可以了:

IF TG_TABLE_NAME = 'scac_proformat'
    THEN
        myTag = '[' || TG_TABLE_NAME ||
            CASE WHEN tg_op IN ('UPDATE','INSERT')
                    THEN coalesce(NEW.format_type,'P')
                    ELSE coalesce(OLD.format_type,'P')
            END
            || ']';
    END IF;     
    IF TG_TABLE_NAME != 'scac_proformat'
    THEN
        myTag = '[' || TG_TABLE_NAME || ']';
    END IF;

我不确定内部 CASE ;)