2012-01-27 27 views
0

空我有一個PG表是這樣的:POSTGRESQL PL/pgSQL的觸發函數返回插入

CREATE TABLE order_status_history (
order_id integer NOT NULL, 
status character varying NOT NULL, 
sequence integer DEFAULT 1 NOT NULL, 
date_status timestamp with time zone DEFAULT now() NOT NULL, 
record_state character varying(12) DEFAULT 'ACTIVE'::character varying NOT NULL 
); 

複合PK上的order_id和序列。 實質上,狀態列是從1開始的每個單獨的order_id的自動遞增值。所以,如果order_id 2已經有兩行,序列將是1和2,對於新行,序列應該是3. 我試圖在插入之前使用觸發器來實現此行爲,但是當我嘗試爲新的order_id插入第一行(即觸發器不必在插入之前更改行),我得到PG的錯誤。說它不能在序列中插入NULL。我看不到我的觸發器函數是如何返回NULL,但我的PL/SQL不是很好,所以我敢肯定這是簡單的...下面的觸發器函數,謝謝。

DECLARE 
    seq_no INTEGER; 

BEGIN 
    SELECT INTO seq_no MAX(sequence) FROM rar.order_status_history WHERE order_id = NEW.order_id; 
    IF FOUND THEN 
     NEW.sequence := seq_no + 1; 
    END IF; 

    RETURN NEW; 
END; 
+1

使用'SELECT max()'獲得一個新的id是一個非常非常糟糕的主意。這不是交易安全的,您的解決方案會在多用戶環境中創建錯誤的序列號。 – 2012-01-27 11:38:41

+0

@a_horse_with_no_name,好的謝謝你,有沒有更好的方法來實現這一目標? – Alpaus 2012-01-27 13:38:12

+0

如果你想使交易安全,你將不得不鎖定表格。 – 2012-01-27 14:05:05

回答

2

如果order_id中沒有order_status_history,那麼MAX(sequence)將爲空。使用COALESCE(MAX(sequence),0),使其默認爲1

你可以簡單地寫:

NEW.sequence := (SELECT COALESCE(MAX(sequence),0) FROM /* etc.. */) + 1; 

在這之前爲好,讓這種多工作,你真的應該鎖定在獨佔模式的順序排同時插入到相同訂單的歷史記錄中的交易。那就是:

SELECT 1 FROM orders WHERE orders.order_id = NEW.order_id FOR UPDATE; 
+0

謝謝,更有意義。我需要釋放鎖還是一旦觸發函數結束後就隱含了?有沒有什麼好的網絡資源可以讓我更好地掌握這些功能?我發現一個很好的綜合教程很難從我發現的東西中拼湊出來。 – Alpaus 2012-01-27 21:47:52

+0

鎖定將在交易結束時自動釋放。儘快釋放它並不合理,因爲其他交易將無法看到鎖定所保護的變化。 – araqnid 2012-01-28 14:50:10

1

max(sequence)總是FOUND,但它可能是NULL如果沒有數據。在這種情況下,您的NEW.sequence := seq_no + 1仍然是NULLIF seq_no IS NOT NULL聽起來像一個更合適的條件。