2016-10-25 88 views
0

假設我有以下表格:觸發引起的更新語句另一個觸發內部

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; 

但我想自動地將新的輸入值在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(); 

則以下語句不應該工作:

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

以下命令插入foo的正確元組,擁有自營= 0

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

但是,當我試圖在bat表中添加新值,數據庫顯示我'foo中的導入列必須與bar表中的值相等。':

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

這裏發生了什麼?兩個觸發器都在每個新行之後執行,所以foo表中的限制不應在最後一條語句中激活。

我該怎麼辦才能修復它?

謝謝!

回答

2

一個AFTER 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

當行級AFTER觸發器被觸發,通過外部指令的所有數據變化都已經完成,對被調用的觸發函數是可見的。


你可以從barbar_insert()值的總和更新foo

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包含其他一些數據,從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; 

你不需要任何觸發器,全部都是自動完成的。

+0

那麼,我該如何解決它? –

+0

所以閱讀編輯答案。 – klin