加入if语句的PostgreSQL触发函数

时间:2016-08-13 01:09:32

标签: postgresql join triggers plpgsql database-trigger

我有一个包含3个触发器的表a,只要插入,更新或删除b中的行,就会在a中插入,更新或删除相应的行。所有3个触发器使用相同的触发器函数p

CREATE OR REPLACE FUNCTION p ()
RETURNS TRIGGER
AS $$
BEGIN
  IF (TG_OP = 'INSERT') THEN
    -- INSERT INTO b ...
    RETURN NEW;
  ELSIF (TG_OP = 'UPDATE') THEN
    -- UPDATE b ...
    RETURN NEW;
  ELSIF (TG_OP = 'DELETE') THEN
    -- DELETE FROM b ...
    RETURN NEW;
  ELSE
    RETURN NULL;
  END IF;
END;
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER i AFTER INSERT ON a FOR EACH ROW EXECUTE PROCEDURE p ();
CREATE TRIGGER u AFTER UPDATE ON a FOR EACH ROW EXECUTE PROCEDURE p ();
CREATE TRIGGER d AFTER DELETE ON a FOR EACH ROW EXECUTE PROCEDURE p ();

a还有一个外键a1c(主键c1),我想以这种方式改变p根据{{​​1}}中的列IF,它还会进入ELSIF / c2分支:如果已加入列已更改,请输入c和{{1分支;如果保持不变,请输入INSERT分支。实际上,这样的事情:

UPDATE

其中UPDATE IF (TG_OP = 'INSERT') OR ((TG_OP = 'UPDATE') AND (oldC.c2 <> newC.c2)) THEN -- ... ELSIF (TG_OP = 'UPDATE') OR (oldC.c2 = newC.c2) THEN -- ... ELSIF (TG_OP = 'DELETE') OR ((TG_OP = 'UPDATE') AND (oldC.c2 <> newC.c2)) THEN -- ... ELSE -- ... END IF; 来自与这些类似的联接(使用大约语法):

oldC

因此,有效需要的是newC语句之外的两个连接,这将允许它引用SELECT oldC.* FROM a, c AS oldC WHERE OLD.a1 = c.c1; SELECT newC.* FROM a, c AS newC WHERE NEW.a1 = c.c1; IF(或类似的东西)。这是可能的吗?oldC的更改版本看起来如何(使用正确的PostgreSQL语法)?

1 个答案:

答案 0 :(得分:3)

首先,NEW的情况下没有DELETE,因此RETURN NEW;没有意义,会引发异常。无论如何,你为AFTER触发器返回的并不重要。也可能是RETURN NULL;

接下来,如果是OLD,则无法引用INSERT。在引用OLDNEW之前,您必须检查的操作类型。

而你现在只需要一个触发器:

CREATE TRIGGER a_i_u_d   -- *one* trigger
AFTER INSERT OR UPDATE OR DELETE ON a
FOR EACH ROW EXECUTE PROCEDURE p ();

但是,我建议INSERTUPDATEDELETE 单独的触发器功能 避免并发症。然后你需要三个独立的触发器,每个触发器调用它们各自的触发器功能。

您要添加的案例只能 影响UPDATE。没有什么可以像您使用INSERTDELETE描述的那样“改变”。

严格地说,即使是UPDATE触发器,你要求的是不可能

  

取决于c2中的列c:如果已加入列已更改...

a的触发器功能只能看到表c快照。无法检测该表中的任何“更改”。 如果 你真的打算写:

  

取决于列a.a1:如果更改,以便引用的值c.c2现在不同...

..然后有办法:

由于BEFORE触发器不太容易出现无限循环和其他并发症,因此我展示了BEFORE UPDATE触发器。 (更改为AFTER是微不足道的。):

CREATE OR REPLACE FUNCTION p_upbef()
  RETURNS trigger AS
$func$
BEGIN
   IF NEW.a1 <> OLD.a1 THEN  -- assuming a1 is defined NOT NULL
      IF (SELECT count(DISTINCT c.c2) > 1  -- covers possible NULL in c2 as well
          FROM   c
          WHERE  c.c1 IN (NEW.a1, OLD.a1)) THEN
            -- do something
      END IF;
   END IF;

   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

如果a1可以为NULL,并且您需要跟踪从/到NULL的更改,则需要执行更多操作...

触发:

CREATE TRIGGER upbef
BEFORE UPDATE ON a
FOR EACH ROW EXECUTE PROCEDURE p_upbef ();

由于现在一切都取决于a.a1的变化(并且触发器中没有其他内容),您可以将外部IF移动到触发器本身(更便宜):

CREATE OR REPLACE FUNCTION p_upbef()
  RETURNS trigger AS
$func$
BEGIN
   IF (SELECT count(DISTINCT c.c2) > 1  -- covers NULL as well
       FROM   c
       WHERE  c.c1 IN (NEW.a1, OLD.a1)) THEN  -- assuming a1 is NOT NULL!
      -- do something
   END IF;

   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

触发:

CREATE TRIGGER upbef
BEFORE UPDATE OF a1 ON a
FOR EACH ROW EXECUTE PROCEDURE p_upbef();

为了我们的目的:仅在相关案例中UPDATE运行昂贵的支票

相关: