Postgresql 触发器在 NEW 和 OLD 记录上运行

Posted

技术标签:

【中文标题】Postgresql 触发器在 NEW 和 OLD 记录上运行【英文标题】:Postgresql trigger operating on both NEW and OLD records 【发布时间】:2018-03-29 19:51:56 【问题描述】:

这主要是一个风格问题。

我有一个 AFTER INSERT OR UPDATE 触发器。我想针对任何可用的 NEW/OLD 记录运行 same 查询。与其让条件检查 TG_OP 并复制查询,确定哪些可用并循环它们的最简洁方法是什么?

示例代码:

CREATE FUNCTION myfunc() RETURNS TRIGGER AS $$
  DECLARE
    id int;
    ids int[];
  BEGIN
    IF TG_OP IN ('INSERT', 'UPDATE') THEN
      ids := ids || NEW.id;
    END IF;

    IF TG_OP IN ('UPDATE', 'DELETE') THEN
      ids := ids || OLD.id;
    END IF;

    FOREACH id IN ARRAY ARRAY(SELECT DISTINCT UNNEST(ids))
    LOOP
      RAISE NOTICE 'myfunc called for % on id %', TG_OP, id;
      /* RUN QUERY REFERENCING id */
    END LOOP;

    RETURN NULL;
  END;
$$ LANGUAGE plpgsql;

是否有更短/更简单/更惯用的方法来实现这一目标?

【问题讨论】:

您在寻找ids := ids || new.id吗? (但你必须将ids初始化为一个空数组,而不是null)详见手册:postgresql.org/docs/current/static/functions-array.html 我打算把它当作伪代码,因为我不知道使用数组是否是最好的方法。不过谢谢。我编辑了代码,所以它现在真的可以工作了。但是有没有更简单的方法?实现这一目标似乎非常复杂。 【参考方案1】:

数组处理和单独的SELECT DISTINCT 对于这项工作来说似乎太昂贵了。这应该更便宜:

CREATE FUNCTION myfunc()
  RETURNS TRIGGER AS
$func$
DECLARE
   _id int;
BEGIN
   CASE TG_OP
      WHEN 'INSERT', 'UPDATE' THEN
         _id := NEW.id;
      WHEN 'DELETE' THEN
         _id := OLD.id;
   END CASE;

   FOR i IN 1..2  -- max 2 iterations
   LOOP
      RAISE NOTICE 'myfunc called for % on id %', TG_OP, _id;
      /* RUN QUERY REFERENCING _id */

      EXIT WHEN TG_OP <> 'UPDATE' OR i = 2;  -- only continue in 1st round for UPDATE  
      EXIT WHEN _id = OLD.id;                -- only continue for different value
      _id := OLD.id;
   END LOOP;
   RETURN NULL;
END
$func$  LANGUAGE plpgsql;

相关:

BREAK statement in PL/pgSQL

但我可能只是为每个 DML 语句编写一个单独的触发器函数和触发器。而是三个非常简单和快速的函数,而不是一个通用但复杂且速度较慢的函数。

【讨论】:

谢谢。是的,这有点复杂。好吧,作为一个必然的问题,你将如何命名/管理不同的对象?您是否有一个执行实际查询的 myfunc(int),然后是 myfunc_new_trig 和 myfunc_old_trig?那里有最佳实践的指针吗? 不是myfunc_new_trig & myfunc_old_trig,因为我将为INSERTUPDATEDELETE 设置三个单独的触发器(以及各自的触发器函数)——UPDATE 触发器是只有一个处理OLD NEW。 (根据myfunc(int) 中的实际查询,所有这些毕竟可能会被简化。但这仍未公开。)

以上是关于Postgresql 触发器在 NEW 和 OLD 记录上运行的主要内容,如果未能解决你的问题,请参考以下文章

使用 :new 和 :old 在更新和插入之前触发 Oracle

触发器中的 :OLD 和 :NEW 变量属于啥数据类型?

在表上实现语句级触发器时,是不是可以获取所有受影响行的 OLD 和 NEW 记录?

oracle new 和old 关键字

关于mysql中触发器old和new

[转]oracle触发器与:new,:old的使用