2016-10-25 1 views
0

Предположим, у меня есть следующие таблицы:Trigger вызвано заявлением обновления внутри другого триггера

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); 

Таким образом, столбец импорта таблицы Foo должна быть равна сумме столбца импорта в баре таблицы.

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; 

Но я хочу автоматически суммировать новое значение импорта в баре с ссылкой на 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(); 

Тогда следующее утверждение не должно работать:

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

Следующая инструкция вставить правильный кортеж для обув, при импорте = 0.

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

Но, когда я пытаясь добавить новые значения в таблицу летучей мыши, база данных показывает, что «столбец импорта в foo должен быть равен в таблице бар».:

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

Что происходит здесь? Оба триггера выполняются после каждой новой строки, поэтому ограничение в таблице foo не должно активироваться в последнем утверждении.

Как это сделать, чтобы исправить это?

Спасибо!

ответ

2

Аппликатор AFTER INSERT запускается после выполнения инструкции insert, а не после ввода каждой строки. Таким образом, ваш триггер должен хорошо работать в операторах, вставляющих одну строку, но не с несколькими вставками. Чтобы проверить это поведение запустить этот скрипт в 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 

Для the documentation:

Когда на уровне строк ПОСЛЕ запуска обжигают, все изменения данных, сделанные внешней командой уже являются полными и видны вызываемой триггерной функции.


Вы можете обновить foo с суммой значений от bar в 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; 

Однако, лучшим решением является вид. Если foo только агрегирует import, удалить таблицу и создать представление вместо:

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

Если foo содержит некоторые другие данные, удалить import из foo и создать вид:

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; 

Вам больше не нужны триггеры, все делается автоматически.

+0

Итак, как я могу это исправить? –

+0

Итак, прочитайте отредактированный ответ. – klin