2017-01-30 61 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约束(基本上,如果所有新路径都有效,则允许新连接,并且这可以相当容易地检查)。

也许最简单的(从使用的角度来看)是完整的路径。如果每个节点都包含从根开始的完整路径,那么在插入时,您可以相当容易地检查现有的完整路径以查找潜在的周期。