2016-08-13 83 views
1

我有一个表a,带有3个触发器,只要插入,更新或删除了a中的一行,就会插入,更新或删除b中的对应行。所有3个触发器都使用相同的触发功能p在if语句中加入PostgreSQL触发函数

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以这样一种方式,它进入IF/ELSIF分支还取决于柱c2c :如果连接的列已更改,请输入INSERTUPDATE分支;如果保持不变,请输入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; 

其中oldCnewC会导致加入类似以下(含约语法):

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声明,这将允许它参考oldCnewC(或类似的东西)。这是可能的,p的修改版本看起来如何(使用正确的PostgreSQL语法)?

回答

3

首先,DELETE没有NEW,所以RETURN NEW;没有意义,会引发异常。没关系不管怎么说你还是会回到AFTER的触发器。也可能是RETURN NULL;

接下来,在INSERT的情况下,您也不能参考OLD。您必须检查之前的参考OLDNEW的操作类型。

而你只需要一个触发你拥有了它,现在的方式:

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

然而,我建议独立的触发功能INSERTUPDATE和为避免并发症,请使用DELETE。然后你需要三个单独的触发器,每个触发器都调用它的相应触发器

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

而且严格来说,你问什么是即使对于UPDATE触发不可能

取决于cc2:如果联接的列改变...

触发功能的表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涉及a1实际上可能离开价值不变,但它不够好,我们的宗旨两种方式:只在相关案例上运行c.c2的昂贵支票。

相关:

+0

+1它是正确的假设,我将不得不放置/诺特尔触发的'C'支持第一种解释(”取决于c中的列c2:如果连接的列改变了“)? – Drux

+1

@Drux:是的,为了监控'c.c2'中的变化,你需要'c'上的触发器。小心不要创建带有太多触发器的无限循环。有时,使用数据修改CTE的组合查询是一个很好的选择。 [实施例](http://stackoverflow.com/questions/20561254/insert-data-in-3-tables-at-a-time-using-postgres/20561627#20561627) –