我有以下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 > D
和A > 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);
答案 0 :(得分:2)
首先,我相信您的表格中有重复的条目。 (A,D)
出现两次。其次,删除边(B,D)
后,应保留以下路径:
(A,A)
,(B,B)
,(C,C)
,(D,D)
(A,B)
(A,C)
(A,D)
(通过C)因此,要删除此示例中的边(B,D)
,只需删除该行:
Delete MyTable
Where Ancestor = 'B'
And Descendant = 'D'
闭包表仍然只映射两个节点之间的关系。它的特殊之处在于它将每个间接关系有效地映射为直接关系。边缘(B,D)
只是说你可以从B
到D
。这一行没有说明你如何到达B
,也没有说明从B
到D
需要多少个节点。它只是说你可以从B
到D
。因此,A > B > D
本身没有列出边缘。相反,所有捕获的内容都是,您可以从A
到B
以及从A
到D
,即使边(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
因此,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查询,这个更新正在逃避我,但理论上这应该工作而不必完全重建闭包表......