简体   繁体   English

由另一个触发器内的更新语句引起的触发器

[英]Trigger caused by an update statement inside another trigger

Suppose I have the following tables: 假设我有下表:

CREATE TABLE foo (
  id_foo int NOT NULL,
  import int NOT NULL,
  CONSTRAINT foo_pk PRIMARY KEY(id_foo)
);

CREATE TABLE bar (
  id_bar int NOT NULL,
  id_foo int NOT NULL,
  import int NOT NULL,
  CONSTRAINT bar_pk PRIMARY KEY(id_bar)
);

ALTER TABLE bar ADD CONSTRAINT fk_bar FOREIGN KEY(id_foo) REFERENCES foo(id_foo);

So, the import column of the foo table must equal the sum of the import column in bar table. 因此,foo表的import列必须等于bar表中的import列的总和。

CREATE OR REPLACE FUNCTION foo_restriction() RETURNS TRIGGER AS
$$
  BEGIN
    IF (NEW.import != (SELECT COALESCE(SUM(import),0) FROM bar WHERE id_foo = NEW.id_foo)) THEN
      RAISE EXCEPTION 'import column in foo must be equal in bar table.';
    END IF;

    RETURN NEW;
  END;
$$
LANGUAGE plpgsql;

But I want to automatically sum the new import value in bar to the foo reference. 但是我想自动将bar中的新导入值求和到foo引用。

CREATE OR REPLACE FUNCTION bar_insert() RETURNS TRIGGER AS
$$
  BEGIN
    UPDATE foo SET import = import + NEW.import WHERE id_foo = NEW.id_foo;
    RETURN NEW;
  END;
$$
LANGUAGE plpgsql;


CREATE TRIGGER TR_fooRestriction AFTER INSERT OR UPDATE ON foo FOR EACH ROW EXECUTE PROCEDURE foo_restriction();
CREATE TRIGGER TR_barInsert AFTER INSERT ON bar FOR EACH ROW EXECUTE PROCEDURE bar_insert();

Then the following statement should not work: 然后,以下语句不起作用:

INSERT INTO foo(id_foo, import) VALUES(1,25); --FAIL. Works ok.

The following instruction insert a correct tuple for foo, with import = 0. 以下指令为foo插入一个正确的元组,import = 0。

INSERT INTO foo(id_foo, import) VALUES(2,0); --OK.

But, when I'm trying to add new values in the bat table, the database show me 'import column in foo must be equal in bar table.': 但是,当我尝试在bat表中添加新值时,数据库显示“在foo中的import列必须在bar表中相等”。

INSERT INTO bar(id_bar, id_foo, import) VALUES
(1,2,100),(2,2,160),(3,2,40);

Whats happening here?, both triggers are executed after each new row, so the restriction in foo table should not be activated in the last statement. 这是怎么回事?两个触发器都在每个新行之后执行,因此foo表中的限制不应在最后一条语句中激活。

How can I do to fix it? 我该如何解决?

Thanks! 谢谢!

An AFTER INSERT trigger is executed after insert statement is executed, not after each row is inserted. AFTER INSERT触发器是在执行insert语句之后而不是在插入每一行之后执行的。 So your trigger should work well in statements inserting a single row, but not with multirow inserts. 因此,触发器在插入单行的语句中应该能很好地工作,但在多行插入中则不能。 To test this behaviour run this script in psql: 要测试此行为,请在psql中运行以下脚本:

drop table if exists test;
create table test (id int);

create or replace function test_trigger()
returns trigger language plpgsql as $$
begin
    raise notice '%', (select count(*) from test);
    return null;
end $$;

create trigger test_trigger 
after insert on test 
for each row execute procedure test_trigger();

insert into test values (1), (2), (3);

DROP TABLE
CREATE TABLE
CREATE FUNCTION
CREATE TRIGGER
NOTICE:  3
NOTICE:  3
NOTICE:  3
INSERT 0 3

For the documentation : 对于文档

When a row-level AFTER trigger is fired, all data changes made by the outer command are already complete, and are visible to the invoked trigger function. 当触发行级AFTER触发器时,外部命令进行的所有数据更改都已完成,并且对调用的触发器函数可见。


You could update foo with a sum of values from bar in bar_insert() : 您可以使用bar_insert() bar的值的总和来更新foo

    update foo 
    set import = (
        select sum(import) 
        from bar where id_foo = new.id_foo)
    where id_foo = new.id_foo;
    return null;

However, the best solution is a view. 但是,最好的解决方案是视图。 If foo only aggregates import , drop the table and create the view instead: 如果foo仅聚合import ,则删除表并创建视图:

create or replace view foo_view as 
    select id_foo, sum(import) as import
    from bar
    group by id_foo;

If foo contains some other data, remove import from foo and create the view: 如果foo包含其他数据,请从foo删除import并创建视图:

create or replace view foo_view as 
    select foo.*, sum(bar.import) as import_total
    from bar
    join foo using(id_foo)
    group by foo.id_foo;

You do not need any triggers, all is done automatically. 您不需要任何触发器,所有操作都是自动完成的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM