视图上带有`BEFORE`或`AFTER`和`UPDATE`或`UPDATE`的触发器在PostgreSQL中无效

vm0i2vca  于 2024-01-07  发布在  PostgreSQL
关注(0)|答案(1)|浏览(172)

我创建了如下所示:

  • tableperson
  • 一个名为logtable,其中插入了一行。
  • my_vview
  • trigger function称为my_func(),其目的是将num.log增加1
  • 一个名为my_ttrigger,它在my_v上执行my_func()beforeeach statementINSERTUPDATEDELETE)。
CREATE TABLE person (
  name VARCHAR(20)
);

CREATE TABLE log (
  num INTEGER
);
INSERT INTO log (num) VALUES (0);

CREATE VIEW my_v AS
  SELECT * FROM person;

CREATE FUNCTION my_func() RETURNS trigger
AS $$
BEGIN
  UPDATE log SET num = num + 1;
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER my_t BEFORE INSERT OR UPDATE OR DELETE ON my_v
FOR EACH STATEMENT EXECUTE FUNCTION my_func();

字符串
另外,我还测试了triggermy_t执行aftereach statement的情况

CREATE TRIGGER my_t AFTER INSERT OR UPDATE OR DELETE ON my_v
FOR EACH STATEMENT EXECUTE FUNCTION my_func();


通过这种设置,表log中的num的值预计会计算my_func()被触发器调用的次数。
my_v中插入、更新或删除行时,log.num仍为0。请参阅下面的语句,最后一个语句是对表log的检查(* 实际上在每次操作后进行了测试,但为了缩短问题,从此处删除 *):

postgres=# INSERT INTO my_v (name) VALUES ('John');
INSERT 0 1 /* -> 1 row successfully inserted */

postgres=# UPDATE my_v SET name = 'Tom';
UPDATE 1 /* -> 1 row successfully updated */

postgres=# DELETE FROM my_v;
DELETE 1 /* -> 1 row successfully deleted */

postgres=# SELECT num FROM log;
 num
-----
   0
(1 row)


因此,视图上带有BEFOREAFTERINSERTUPDATEDELETE的触发器在PostgreSQL中不起作用。

重要提示my_t触发器的另一个版本INSTEAD OFFOR EACH ROW可以正常工作:

CREATE TRIGGER my_t INSTEAD OF INSERT OR UPDATE OR DELETE ON my_v
FOR EACH ROW EXECUTE FUNCTION my_func();


如何创建my_t触发器,在my_v上发生INSERTUPDATEDELETE操作之前或之后触发my_func()

hc2pp10m

hc2pp10m1#

这是设计出来的,虽然不是很明显,但它是有文档记录的。Views 上的BEFORE / AFTER触发器只支持那些具有INSTEAD OF触发器的视图。

CREATE TRIGGER

  • (示例前的最后一段)*

仅当视图上的操作由行级INSTEAD OF触发器处理时,才会激发视图上的语句级触发器。如果操作由INSTEAD规则处理,则执行该规则发出的任何语句,以代替命名视图的原始语句,这样,将激发的触发器是在替换语句中命名的表上的触发器。同样,如果视图可自动更新,然后,通过自动将语句重写为视图基表上的操作来处理操作,以便基表的语句级触发器被触发。
感谢@Damien_The_Unbeliever识别此引用
我们可以在小提琴上演示这一点:https://www.db-fiddle.com/f/Ki2fBnAKC4rH1kMs66UaT/0

CREATE TABLE person (
  name VARCHAR(20)
);

CREATE TABLE log (
  BeforeViewCounter INTEGER,
  InsteadOfViewCounter INTEGER,
  BeforeTableCounter INTEGER
);
INSERT INTO log (BeforeViewCounter, InsteadOfViewCounter, BeforeTableCounter) VALUES (0,0,0);

CREATE VIEW my_v AS
  SELECT * FROM person;

CREATE FUNCTION beforeView_func() RETURNS trigger AS 
'
BEGIN
  UPDATE log SET BeforeViewCounter = BeforeViewCounter + 1;
  RETURN NULL;
END;
' 
LANGUAGE plpgsql;
CREATE FUNCTION insteadView_func() RETURNS trigger AS 
'
BEGIN
  UPDATE log SET InsteadOfViewCounter= InsteadOfViewCounter + 1;
  
  IF (TG_OP = ''DELETE'') THEN
    DELETE FROM person WHERE name = OLD.name; 
    IF NOT FOUND THEN RETURN NULL; END IF;
    RETURN OLD;
  ELSIF (TG_OP = ''UPDATE'') THEN
    UPDATE person SET name = NEW.name WHERE name = OLD.name;
    RETURN NEW;
  ELSIF (TG_OP = ''INSERT'') THEN
    INSERT INTO person SELECT NEW.*;
    RETURN NEW;
  END IF;
  RETURN NULL; 
END;
' 
LANGUAGE plpgsql;
CREATE FUNCTION beforeTable_func() RETURNS trigger AS 
'
BEGIN
  UPDATE log SET BeforeTableCounter= BeforeTableCounter + 1;
  RETURN NEW;
END;
' 
LANGUAGE plpgsql;

CREATE TRIGGER before_t BEFORE INSERT OR UPDATE OR DELETE ON my_v
FOR EACH STATEMENT EXECUTE FUNCTION beforeView_func();

CREATE TRIGGER beforeTable_t BEFORE INSERT OR UPDATE OR DELETE ON person
FOR EACH ROW EXECUTE FUNCTION beforeTable_func();

CREATE TRIGGER intead_t INSTEAD OF INSERT OR UPDATE OR DELETE ON my_v
FOR EACH ROW EXECUTE FUNCTION insteadView_func();


INSERT INTO my_v (name) VALUES ('John');
SELECT * FROM log FULL OUTER JOIN my_v ON 1=1;
UPDATE my_v SET name = 'Tom';
SELECT * FROM log FULL OUTER JOIN my_v ON 1=1;
DELETE FROM my_v;
SELECT * FROM log FULL OUTER JOIN my_v ON 1=1;

字符串

  • 删除处理程序中有一个缺陷,最终名称应该是null,但它对本演示并不重要。*

| 前视计数器|代替视图计数器|柜台前|名称|
| --|--|--|--|
| 3 | 3 | 3 |汤姆|
如果您运行相同的测试,这次删除了INSTEAD OF触发器https://www.db-fiddle.com/f/jE6tbJAGTGRqpYKgXW346n/1,则只有表上的触发器会触发
| 前视计数器|代替视图计数器|柜台前|名称|
| --|--|--|--|
| 0 | 0 | 3 |null个|
虽然这看起来像是一个奇怪的实现,但其背后的目的是针对只能在INSTEAD OF触发器中定义的复杂视图扩展对UPDATE/UPDATE/UPDATE的支持。子系统将在为每个ROW执行INSTEAD OF触发器之前 * 调用一次语句级别的BEFORE触发器。在操作结束时,将调用AFTER语句触发器。
其他RDBMS一般不支持视图上的触发器。这不是为了允许在任何任意视图上执行BEFORE/AFTER,而是为了专门执行预处理。条件,这些条件可以阻止INSTEAD OF ROW语句的执行,而且因为ROW操作的顺序是不确定的,所以它允许扩展性站点帮助您审核或跟踪整个批处理,而不是尝试设计解决方案,尝试跟踪FOR EACH ROW内的第一行/最后一行之间的差异。

相关问题