2017-08-04 32 views
0

基本上我有table_a和table_b。 table_b由來自table_a的特徵構成,並且通常它們共享section_id列和狀態。當另一個表被插入或更新時,Postgresql觸發器更新表中的列

表-A的SECTION_ID是主鍵,所以這是唯一的,但表-B可以有多個section_ids,但他們都有着同樣的地位

用戶插入和更新表-B,我想捕捉狀態更改回表-A

CREATE TRIGGER table_b_aiu 
AFTER INSERT OR UPDATE 
ON table_b 
FOR EACH ROW 
WHEN (((new.status = 100) OR (new.status = 200))) 
EXECUTE PROCEDURE table_b_aiu(); 

CREATE OR REPLACE FUNCTION table_b_aiu() 
RETURNS trigger AS 
$BODY$ 
BEGIN 
UPDATE table_a a 
SET status = 100 
FROM table_b b 
WHERE (b.status = 100 or b.status = 200) 
AND a.section_id = b.section_id; 
    RETURN new; 
END; 
$BODY$ 
LANGUAGE plpgsql VOLATILE 
COST 100; 

問題在於,它不是隻更新最新更新或插入的行,而是更新整個數據集,這並非意圖。如何僅更新新行?我試圖把爭論的TRIGER,但它不接受new.section_id

+0

是'section_id'獨具一格。行的標識符?對th的定義有幫助還有問題的表格。 – cole

+0

@ C.Arendt section_id是table_a的主鍵,但不是table_b的 – Luffydude

+1

您是什麼意思的觸發器參數?像這樣:'EXECUTE PROCEDURE table_b_aiu(new.section_id);'? 您有權訪問該行而不將其作爲參數傳遞。您可以簡單地執行'where a.section_id = new.section_id'並從'table_b'中移除'因爲它不是必需的。 –

回答

1

所以有幾件事。對於這種類型的問題,訪問測試表格定義肯定有幫助。我嘲笑了我期望的桌子設計。此外,包含「所需行爲」(即,是否打算始終將狀態設置爲100?無論table_b發生什麼情況)都很有幫助。此外,update聲明必然會遇到整個table_a,這就是爲什麼您需要唯一約束才能獲得唯一記錄。如果您錯誤地將狀態設置爲100,那麼它應該是SET status = new.status,這將是一個不同的問題(但可能看起來更新是「擊中整個表」,請參閱下面的示例 - 特別是section_id=3)。

希望以下說明準確的行爲。我懷疑你的觸發器應該設置status=new.status

CREATE TABLE table_a 
(
section_id serial 
, status integer 
, CONSTRAINT pk_table_a PRIMARY KEY (section_id) 
); 

CREATE TABLE table_b 
(
id serial 
, section_id integer 
, status integer 
, CONSTRAINT pk_table_b_aiu PRIMARY KEY (id) 
) 
; 


CREATE OR REPLACE FUNCTION table_b_aiu() 
RETURNS trigger AS 
$BODY$ 
BEGIN 
UPDATE table_a a 
SET status = 100 -- intentional?? 
WHERE (new.status = 100 or new.status = 200) 
AND a.section_id = new.section_id; 

    RETURN new; 
END; 
$BODY$ 
LANGUAGE plpgsql VOLATILE 
COST 100; 

CREATE TRIGGER table_b_aiu 
AFTER INSERT OR UPDATE 
ON table_b 
FOR EACH ROW 
WHEN (((new.status = 100) OR (new.status = 200))) 
EXECUTE PROCEDURE table_b_aiu(); 


INSERT INTO table_a (section_id, status) 
values (1,100) 
, (2,200) 
, (3,201) 
, (4, 202) 
returning *; 

| section_id | status | 
| 1 | 100 | 
| 2 | 200 | 
| 3 | 201 | 
| 4 | 202 | 

INSERT INTO table_b (section_id, status) 
values (1,101), (2,100), (3,200), (4,201) 
returning *; 

| id | section_id | status | 
| 1 | 1 | 101 | 
| 2 | 2 | 100 | 
| 3 | 3 | 200 | 
| 4 | 4 | 201 | 

select * 
from table_a; 

| section_id | status | 
| 1 | 100 | 
| 4 | 202 | 
| 2 | 100 | 
| 3 | 100 | 

注:new.status in (100,200)是多餘的,但我想你想成爲安全(萬一有人曾是建立一個觸發沒有when聲明

我的建議:如果你想對錶本身的狀態選項限制二百分之百,我建議建立一個外鍵的status表,只有具有這些選項

+0

好的,所以預期的行爲是在table_a上留下狀態100,無論如何。唯一的一個場合,爲什麼我想將狀態從100更改爲其他內容,如果table_b上的行無效並被刪除,但我想這是另一個觸發器 – Luffydude

+1

您肯定可以將該邏輯放在同一個觸發器中。總的來說,我認爲這樣會更好,但如果可能以避免觸發的方式進行關係設計,那麼這可能還是可取的。上述工作是否符合您的期望? (如果你需要測試,你可以'SET search_path = otherschema;'來避免你已經有定義表的模式)。 – cole

+1

還有一個想法 - 您將用戶插入日誌表並更新父級。可以讓用戶插入或更新父表並使用觸發器創建日誌表(這是我通常遵循的方法)。見倒數第二個例子[這裏](https://www.postgresql.org/docs/9.4/static/sql-createtrigger.html) – cole

1

試試這個功能(雖然沒有測試)

CREATE OR REPLACE FUNCTION table_b_aiu() 
RETURNS trigger AS 
$BODY$ 
BEGIN 
    UPDATE table_a 
    SET status = 100 
    WHERE section_id = NEW.section_id 
    RETURN new; 
END; 
$BODY$ 

直到section_idtable_a主鍵應該是足夠了。

相關問題