加入 PostgreSQL 触发器函数的 if 语句
Joins around if statement for PostgreSQL trigger function
我有一个 table a
,它有 3 个触发器,每当插入、更新 a
中的一行时,它们就会插入、更新或删除 b
中的相应行,或删除。所有 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
它进入 IF
/ELSIF
分支的方式也取决于 c
中的列 c2
:如果连接的列发生更改,请输入 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
和 newC
(或类似的东西)。这可能吗? p
的更改版本看起来如何(使用正确的 PostgreSQL 语法)?
首先,在 DELETE
的情况下没有 NEW
,所以 RETURN NEW;
没有意义。这会在 Postgres 11 之前引发异常,这是 changed:
- In PL/pgSQL trigger functions, the
OLD
and NEW
variables now read as NULL when not assigned (Tom Lane)
Previously, references to these variables could be parsed but not
executed.
没关系什么你return为AFTER
触发无论如何。还不如 RETURN NULL;
在 INSERT
的情况下也没有 OLD
。
你只需要一个单触发你现在拥有它的方式:
CREATE TRIGGER a_i_u_d -- *one* trigger
AFTER INSERT OR UPDATE OR DELETE ON a
FOR EACH ROW EXECUTE FUNCTION p ();
但是,我建议单独的触发函数 for INSERT
,UPDATE
和 DELETE
以避免并发症。然后你需要三个独立的触发器,每个触发器调用其各自的触发器函数。
您要添加的案例仅影响UPDATE
。没有什么可以像您用 INSERT
或 DELETE
描述的那样“改变”。严格来说,你要求的是 不可能 即使是 UPDATE
触发器:
depending on a column c2
in c
: if that joined column changed ...
table a
上的触发器函数只能看到 table c
的 单个 快照。无法检测到 table 中的任何“变化”。 如果你真的想写:
depending on column a.a1
: if that changed so that the referenced value c.c2
is different now ...
..那么有一个办法:
由于 BEFORE
触发器不太容易出现无限循环和其他并发症,因此我演示了 BEFORE UPDATE
触发器。 (更改为 AFTER
是微不足道的。):
CREATE OR REPLACE FUNCTION p_upbef()
RETURNS trigger
LANGUAGE plpgsql 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$;
如果 a1
可以为 NULL,并且您还需要跟踪从/到 NULL 的变化,您需要做更多的事情...
触发器:
CREATE TRIGGER upbef
BEFORE UPDATE ON a
FOR EACH ROW EXECUTE FUNCTION p_upbef ();
因为现在一切都取决于 a.a1
的变化(并且触发器中没有其他东西),您可以将外部 IF
移动到触发器本身(更便宜):
CREATE OR REPLACE FUNCTION p_upbef()
RETURNS trigger
LANGUAGE plpgsql 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$;
触发器:
CREATE TRIGGER upbef
BEFORE UPDATE OF a1 ON a -- !
FOR EACH ROW EXECUTE FUNCTION p_upbef();
它完全不一样,因为 UPDATE
涉及 列 a1
实际上可能离开值不变,但对于我们的目的来说,这两种方式都足够好:仅运行在相关情况下对c.c2
进行昂贵的检查。
相关:
- How To Avoid Looping Trigger Calls In PostgreSQL 9.2.1
- Cycloning in trigger
我有一个 table a
,它有 3 个触发器,每当插入、更新 a
中的一行时,它们就会插入、更新或删除 b
中的相应行,或删除。所有 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
它进入 IF
/ELSIF
分支的方式也取决于 c
中的列 c2
:如果连接的列发生更改,请输入 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
和 newC
(或类似的东西)。这可能吗? p
的更改版本看起来如何(使用正确的 PostgreSQL 语法)?
首先,在 DELETE
的情况下没有 NEW
,所以 RETURN NEW;
没有意义。这会在 Postgres 11 之前引发异常,这是 changed:
- In PL/pgSQL trigger functions, the
OLD
andNEW
variables now read as NULL when not assigned (Tom Lane)Previously, references to these variables could be parsed but not executed.
没关系什么你return为AFTER
触发无论如何。还不如 RETURN NULL;
在 INSERT
的情况下也没有 OLD
。
你只需要一个单触发你现在拥有它的方式:
CREATE TRIGGER a_i_u_d -- *one* trigger
AFTER INSERT OR UPDATE OR DELETE ON a
FOR EACH ROW EXECUTE FUNCTION p ();
但是,我建议单独的触发函数 for INSERT
,UPDATE
和 DELETE
以避免并发症。然后你需要三个独立的触发器,每个触发器调用其各自的触发器函数。
您要添加的案例仅影响UPDATE
。没有什么可以像您用 INSERT
或 DELETE
描述的那样“改变”。严格来说,你要求的是 不可能 即使是 UPDATE
触发器:
depending on a column
c2
inc
: if that joined column changed ...
table a
上的触发器函数只能看到 table c
的 单个 快照。无法检测到 table 中的任何“变化”。 如果你真的想写:
depending on column
a.a1
: if that changed so that the referenced valuec.c2
is different now ...
..那么有一个办法:
由于 BEFORE
触发器不太容易出现无限循环和其他并发症,因此我演示了 BEFORE UPDATE
触发器。 (更改为 AFTER
是微不足道的。):
CREATE OR REPLACE FUNCTION p_upbef()
RETURNS trigger
LANGUAGE plpgsql 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$;
如果 a1
可以为 NULL,并且您还需要跟踪从/到 NULL 的变化,您需要做更多的事情...
触发器:
CREATE TRIGGER upbef
BEFORE UPDATE ON a
FOR EACH ROW EXECUTE FUNCTION p_upbef ();
因为现在一切都取决于 a.a1
的变化(并且触发器中没有其他东西),您可以将外部 IF
移动到触发器本身(更便宜):
CREATE OR REPLACE FUNCTION p_upbef()
RETURNS trigger
LANGUAGE plpgsql 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$;
触发器:
CREATE TRIGGER upbef
BEFORE UPDATE OF a1 ON a -- !
FOR EACH ROW EXECUTE FUNCTION p_upbef();
它完全不一样,因为 UPDATE
涉及 列 a1
实际上可能离开值不变,但对于我们的目的来说,这两种方式都足够好:仅运行在相关情况下对c.c2
进行昂贵的检查。
相关:
- How To Avoid Looping Trigger Calls In PostgreSQL 9.2.1
- Cycloning in trigger