我有一个包含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
还有一个外键a1
到c
(主键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语法)?
答案 0 :(得分:3)
首先,NEW
的情况下没有DELETE
,因此RETURN NEW;
没有意义,会引发异常。无论如何,你为AFTER
触发器返回的并不重要。也可能是RETURN NULL;
接下来,如果是OLD
,则无法引用INSERT
。在引用OLD
或NEW
之前,您必须检查的操作类型。
而你现在只需要一个单触发器:
CREATE TRIGGER a_i_u_d -- *one* trigger
AFTER INSERT OR UPDATE OR DELETE ON a
FOR EACH ROW EXECUTE PROCEDURE p ();
但是,我建议INSERT
,UPDATE
和DELETE
的 单独的触发器功能 避免并发症。然后你需要三个独立的触发器,每个触发器调用它们各自的触发器功能。
您要添加的案例只能 影响UPDATE
。没有什么可以像您使用INSERT
或DELETE
描述的那样“改变”。
严格地说,即使是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
运行昂贵的支票。
相关: