移动与多个父母的关闭表

时间:2012-03-09 22:38:26

标签: sql directed-acyclic-graphs transitive-closure-table

我有以下DAG

A --> B

|     |
v     v

C --> D

这是闭包表

| Ancestor | Descendant | Depth |
---------------------------------
| A        | A          | 0     |
| A        | B          | 1     |
| A        | C          | 1     |
| A        | D          | 2     |
| A        | D          | 2     |
| B        | B          | 0     |
| B        | D          | 1     |
| C        | C          | 0     |
| C        | D          | 1     |
| D        | D          | 0     |

如何在不删除B > DA > B > D的情况下删除路径A > C > D(从而删除C > D)。

现在我正在使用以下查询,但它仅在每个节点只有1个父节点时才有效。

DELETE FROM `Closure`
WHERE `Descendant` IN (SELECT `Descendant` FROM `Closure` WHERE `Ancestor`=@Node)
AND `Ancestor` NOT IN (SELECT `Descendant` FROM `Closure` WHERE `Ancestor`=@Node);

3 个答案:

答案 0 :(得分:2)

首先,我相信您的表格中有重复的条目。 (A,D)出现两次。其次,删除边(B,D)后,应保留以下路径:

  1. 节点自我映射:(A,A)(B,B)(C,C)(D,D)
  2. (A,B)
  3. (A,C)
  4. (A,D)(通过C)
  5. 因此,要删除此示例中的边(B,D),只需删除该行:

    Delete MyTable 
    Where Ancestor = 'B'
        And Descendant = 'D'
    

    闭包表仍然只映射两个节点之间的关系。它的特殊之处在于它将每个间接关系有效地映射为直接关系。边缘(B,D)只是说你可以从BD。这一行没有说明你如何到达B,也没有说明从BD需要多少个节点。它只是说你可以BD。因此,A > B > D本身没有列出边缘。相反,所有捕获的内容都是,您可以从AB以及从AD,即使边(B,D)是除去。

答案 1 :(得分:1)

在自然语言中,那就是:“如果除了B之外没有D的父母也是A的后代,则删除祖先 - 后代与D的关系”。这是对的吗?

编辑:不,这不正确;不仅必须删除与D的关系,还要与 D的每个后代重新关系。因此,该标准不是有效...)

我的暂定SQL将是:

DELETE a
FROM Closure AS a
    INNER JOIN Closure AS b ON a.Descendant = b.Descendant
WHERE
    a.Descendant IN (SELECT Descendant FROM Closure WHERE Ancestor = {Child}) AND
    b.Depth = 1 AND
    b.Ancestor != {Parent} AND
    a.Ancestor NOT IN (SELECT Ancestor FROM Closure WHERE Descendant = b.Ancestor)

(对不起,如果我的查询错误 - 或者使用了非标准功能 - 我实际上并没有这方面的经验。但是我的自然语言描述应该能够提供对查询实际需要的洞察力)


更新:第二个想法,我不相信我的查询会适用于所有情况。考虑一下:

A --> B --> D --> E --> F
  1. F是D(True)的后代
  2. E是F(True)
  3. 的父级
  4. E不是B(True)
  5. A不是E(False)的祖先
  6. 因此,A >> F将不被删除,即使它应该被删除。抱歉,我无能为力,但这似乎是一个太大而无法适应单个查询的问题。我建议先找一个算法解决方案,然后看看如何在你的情况下实现。

答案 2 :(得分:1)

鉴于您目前的型号,我不确定它是否可行。我建议您添加一列来计算路径数量,以跟踪从任何节点X到节点Y的不同方式。

所以而不是你的桌子最终

| Ancestor | Descendant | Depth | Refs  |
-----------------------------------------
| A        | A          | 0     | 1     |
| A        | B          | 1     | 1     |
| A        | C          | 1     | 1     |
| A        | D          | 2     | 2     |
| B        | B          | 0     | 1     |
| B        | D          | 1     | 1     |
| C        | C          | 0     | 1     |
| C        | D          | 1     | 1     |
| D        | D          | 0     | 1     |

删除节点将需要更新语句,后跟delete语句。 更新将删除该条目的引用数,而不是删除它找到的条目。然后你可以删除之后带有0或更少引用的条目。

提出一个SQL查询,这个更新正在逃避我,但理论上这应该工作而不必完全重建闭包表......