2017-01-30 84 views
3

我有一個表:PG檢查親子驗證

CREATE TABLE MENUPOINT (
    id BIGINT NOT NULL, 
    parent BIGINT, 
    name VARCHAR(64), 
    CONSTRAINT "MENUPOINT_pkey" PRIMARY KEY(id), 
    CONSTRAINT fkc75dac36251dd346 FOREIGN KEY (parent) 
    REFERENCES MENUPOINT(id) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION 
    NOT DEFERRABLE 
); 

而這個內容:

id  parent name 
------------------------ 
1  null  root 
2  1   child 

這一切都建立這種結構:

root 
+- child 

現在我需要檢查數據庫以檢查是否無法執行:

UPDATE MENUPOINT SET parent = 2 WHERE id = 1; 

因爲:

  1. 我無法找出誰是根。
  2. 樹的顯示是無窮無盡這樣的:


root 
    +- child 
     +- root 
      +- child 
       +- root .... 

我有什麼:

CONSTRAINT "NOT_SELF_REFERENCE" CHECK (id <> parent) 

但它不檢查整個樹。

非循環樹必須改變什麼?

+0

撤消角色對該表的更新。它應該完全使用某種功能來完成。 –

+0

觸發器檢查'NEW.parent不在(從menupoint中選擇id,其中parent = NEW.id)'? –

+0

@VaoTsun只檢查2個元素的循環。使用遞歸CTE可以檢測到任何循環。 - 但是簡單的觸發檢查可能會在高併發的競態條件下運行(f.ex.2同時更新可能會創建一個循環,而它們自己卻不會)。但我不確定'CONSTRAINT TRIGGER'是如何行事的(在文檔中沒有發現與他們有關的競爭條件相關的任何內容)。 – pozs

回答

2

這是太長的評論。

如果你正在存儲分層數據,那麼這個帖子是一個很好的地方start。你也可以谷歌「卡爾文等級樹」,因爲比爾卡爾文已經深入調查了這個問題。

想要做什麼,立即想到三件事情。首先是編寫一個函數來檢查週期並將其用於觸發器insertupdate。這不是我最喜歡的選擇。

另一種選擇是使用閉合表。這列出了樹中兩個節點之間的所有鏈接。然後可以使用修改後的check約束(基本上,如果所有新路徑都有效,則允許新連接,並且這可以相當容易地檢查)。

也許最簡單的(從使用的角度來看)是完整的路徑。如果每個節點都包含從根開始的完整路徑,那麼在插入時,您可以相當容易地檢查現有的完整路徑以查找潛在的週期。