2017-06-13 17 views
0

我正在使用PostgreSQL來使用鄰接列表方法對樹數據結構進行建模。我想是能夠存儲每片葉子上指示哪個組這棵樹屬於額外的數據:驗證樹建模爲鄰接列表的最佳方法,TRIGGER與CHECK約束

CREATE TABLE groups (
    group_id integer PRIMARY KEY 
); 

CREATE TABLE leafs (
    leaf_id integer PRIMARY KEY, 
    parent_id integer REFERENCES leafs ON DELETE CASCADE ON UPDATE CASCADE, 

    group_id integer REFERENCES groups ON DELETE CASCADE ON UPDATE CASCADE NOT NULL 
); 

我也想確保每個葉子只能連接到同一組。看起來這可以通過創建TRIGGERCHECK約束來完成。我有兩個問題:

  1. 什麼是處理這種特殊情況下,TRIGGERCHECK約束的最有效/正確的方法? (以及在這兩者之間進行選擇的經驗法則是什麼)

  2. 是否有更好的方法來強制執行此模型的一致性(或者可能是替代方法來模擬這些樹組)。

感謝,

代碼TRIGGER版本:

CREATE OR REPLACE FUNCTION after_leaf_update() 
    RETURNS trigger AS 
$$ 
BEGIN 
IF (NEW.parent_id IS NOT NULL) AND (SELECT group_id FROM leafs WHERE leaf_id=NEW.parent_id) <> NEW.group_id THEN 
    RAISE EXCEPTION 'group_id of node/leaf does not match!!!'; 
END IF; 
RETURN NEW; 
END; 
$$ 
LANGUAGE 'plpgsql'; 


CREATE TRIGGER leafs_consistency_check 
    BEFORE INSERT OR UPDATE 
    ON leafs 
    FOR EACH ROW 
    EXECUTE PROCEDURE after_leaf_update(); 

代碼CHECK約束版本:

CREATE OR REPLACE FUNCTION leafs_consistency_check_constraint(prn_id integer, grp_id integer) RETURNS BOOL AS 
$$ 
BEGIN 
    IF (prn_id IS NOT NULL) AND (SELECT group_id FROM leafs WHERE leaf_id=prn_id) <> grp_id THEN 
    RAISE EXCEPTION 'CHECK: group_id of node/leaf does not match!!!'; 
END IF; 
RETURN TRUE; 

END; 
$$ LANGUAGE plpgsql; 

ALTER TABLE leafs ADD constraint group_id_constraint check (leafs_consistency_check_constraint(parent_id, group_id)); 
+0

你檢查了[PostgreSQL ltree模塊](https://www.postgresql.org/docs/current/static/ltree.html)嗎?它適用嗎?你能像'CREATE TABLE test(路徑ltree,YOUR_LABEL_HERE文本不是NULL)那樣做''就像'F.21.4。 Example'?通過使用「CREATE EXTENSION ltree」啓用數據庫模塊。 – flutter

+0

你檢查了這個[SO問題]的答案嗎(https://stackoverflow.com/questions/14543173/postgresql-designing-a-tree-hierarchy-with-mixed-node-types-inheritance-does)?它有幫助嗎? – flutter

+0

@ flutter - 感謝您的鏈接!我不知道ltree模塊 - 我會檢查出來! – Yatima

回答

1

使用觸發器。

一般來說,Postgres開發人員不建議在CHECK約束中放置運行查詢的函數。除此之外,在某些情況下,對於查詢,CHECK約束可能會多次檢查,這是一個相當昂貴的檢查。更重要的是,圍繞行可視性和類似的觸發器注意事項進行了許多仔細的設計,這些對於約束條件來說根本不存在。

我還建議您考慮一種替代方法:如果您使用BEFORE觸發器而忽略用戶輸入,並自動爲新節點提供與其父節點相同的group_id,而不是拋出錯誤?

甚至更​​好,因爲group_ids與父母而不是孩子有關係,如何將group_ids放入與父節點鏈接的單獨表中?

+0

謝謝你的答案和解釋背後的理性! Re'替代方法':是的,我確實考慮過這個,但是在這個應用程序嘗試插入錯誤的'group_id'節點時,很明顯的跡象表明某些事情發生了可怕的錯誤(或者對系統的攻擊),所以我認爲錯誤更合適辦法。 – Yatima

+0

將group_ids重新分配到一個單獨的表中:是的,我確實考慮過這一點。我同意這種方式沒有數據重複,因此沒有出錯的餘地。然而,在這樣的架構下,操作變得相當昂貴:[a]識別樹中的所有樹葉,[b]刪除樹。後來是特別麻煩的,因爲它可能形成一個「圓形樹」刪除它似乎並不簡單。我明白[a]可以通過使用遞歸查詢來解決,所以我唯一關心的是[b]。對此有何建議/想法?謝謝, – Yatima