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.
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.
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.
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.':
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.
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. 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:
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.
You could update foo
with a sum of values from bar
in bar_insert()
:
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:
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:
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.