Dru*_*rux 2 postgresql triggers join plpgsql database-trigger
我有一个a包含 3 个触发器的表,每当插入、更新或删除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 ();
Run Code Online (Sandbox Code Playgroud)
a还有一个外键a1进入c(带有主键c1),我想p以这样的方式进行更改,使其进入IF/ELSIF分支也取决于c2中的列c:如果该连接列发生更改,请输入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;
Run Code Online (Sandbox Code Playgroud)
其中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;
Run Code Online (Sandbox Code Playgroud)
因此,实际上需要的是IF语句外部的两个连接,这将允许它引用oldCand newC(或类似的东西)。这可能吗?修改后的版本会如何p(使用正确的 PostgreSQL 语法)?
首先,没有NEWa 的情况DELETE,所以RETURN NEW;没有意义。这会在 Postgres 11 之前引发异常,该异常已更改:
- 在 PL/pgSQL 触发器函数中,
OLD和NEW变量现在在未分配时读取为 NULL (Tom Lane)以前,对这些变量的引用可以被解析但不能被执行。
无论如何,你返回什么触发器并不重要AFTER。也可能是RETURN NULL;
OLD的情况下也没有INSERT。
您只需要一个触发器,就像现在一样:
CREATE TRIGGER a_i_u_d -- *one* trigger
AFTER INSERT OR UPDATE OR DELETE ON a
FOR EACH ROW EXECUTE FUNCTION p ();
Run Code Online (Sandbox Code Playgroud)
不过,我建议为、和单独触发函数以避免复杂化。然后您需要三个单独的触发器,每个触发器调用其各自的触发器函数。INSERTUPDATEDELETE
您要添加的案例只能影响UPDATE. 没有什么可以像您用INSERT或描述的那样“改变” DELETE。严格来说,即使对于触发器,您所要求的也是不可能的UPDATE:
取决于
c2中的列c:如果该连接列发生更改...
表上的触发器函数a只能看到表的单个c快照。无法检测该表中的任何“更改”。如果你真的想写:
取决于列
a.a1:如果发生变化,则引用值c.c2现在不同......
..那么有一个方法:
由于BEFORE触发器不太容易出现无限循环和其他并发症,因此我演示了一个BEFORE UPDATE触发器。(更改为AFTER很简单。):
CREATE TRIGGER a_i_u_d -- *one* trigger
AFTER INSERT OR UPDATE OR DELETE ON a
FOR EACH ROW EXECUTE FUNCTION p ();
Run Code Online (Sandbox Code Playgroud)
如果a1可以为 NULL 并且您还需要跟踪从/到 NULL 的更改,您需要做更多...
扳机:
CREATE TRIGGER upbef
BEFORE UPDATE ON a
FOR EACH ROW EXECUTE FUNCTION p_upbef ();
Run Code Online (Sandbox Code Playgroud)
由于一切都取决于a.a1现在的变化(并且触发器中没有其他东西),因此您可以将外部移动IF到触发器本身(更便宜):
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$;
Run Code Online (Sandbox Code Playgroud)
扳机:
CREATE TRIGGER upbef
BEFORE UPDATE OF a1 ON a -- !
FOR EACH ROW EXECUTE FUNCTION p_upbef();
Run Code Online (Sandbox Code Playgroud)
它并不完全相同,因为UPDATE 涉及列a1实际上可能会使值保持不变,但无论哪种方式都足以满足我们的目的:仅在相关情况下运行昂贵的检查c.c2。
有关的:
| 归档时间: |
|
| 查看次数: |
5131 次 |
| 最近记录: |