虽然跟踪深度并同时允许多个父母可能是可能的,但我从中得到了一种代码味道,尤其是当解决方案涉及重复对时。托马斯的回答概述了这个问题。
我将把问题简化一点,只专注于在允许多个父节点时取消链接节点,因为这本身就是一个棘手的问题。我完全放弃了深度列,并假设没有重复的对。
您必须考虑 D 的孩子,这会很快变得复杂。假设我们有:
A --> B
| |
v v
C --> D --> E
我们想从 B 中断开 D 的链接,这意味着我们还必须删除 E 和 B 之间的链接。但是如果它们是这样连接的呢:
A --> B --> C
| |
v v
D --> E < -- G
|
V
H --> F
在这种情况下,如果我们断开 B > D,我们不想再断开 B 和 E 的链接,因为 E 仍然通过 C 链接到 B。但是我们确实想断开 F 与 B 的连接。
我将使用第二个示例在下面介绍我的解决方案。我知道在这个例子中 D 只有一个父母,但如果 D 有多个父母,它仍然可以正常工作;通过这种方式,我可以更轻松地演示一些边缘案例,这就是我这样做的原因。
这是表格的样子:
| Ancestor | Descendant |
-------------------------
| A | A |
| A | B |
| A | C |
| A | D |
| A | E |
| A | F |
| B | B |
| B | C |
| B | D |
| B | E |
| B | F |
| C | C |
| C | E |
| D | D |
| D | E |
| D | F |
| E | E |
| F | F |
| G | G |
| G | E |
| H | H |
| H | F |
查询1:获取D的所有后代,包括D
SELECT `Descendant`
FROM `Closure`
WHERE `Ancestor` = @Node
这将返回:D, E, F
查询2:获取B的所有祖先,包括B
SELECT `Ancestor`
FROM `Closure`
WHERE `Descendant` = @ParentNode
这将返回:A, B
查询 3a:获取查询 1 中未出现在查询 1 或 2 中的项目的所有祖先
SELECT DISTINCT `Ancestor`
FROM `Closure`
WHERE `Descendant` IN (@Query1)
AND `Ancestor` NOT IN (@Query1)
AND `Ancestor` NOT IN (@Query2)
这将返回:C, G, H
这里的目标是让 E 和 F 的所有父级可能重新连接到更远的链上。
查询 3b:这与查询 3a 完全相同,除了它返回祖先和后代
SELECT DISTINCT `Ancestor`, `Descendant`
[ ... ]
这将返回:(C, E), (G, E), (H, F)
我们稍后会需要这个。
查询 4:将查询 3a 的结果过滤到重新连接到链上更远的节点
SELECT `Ancestor`,`Descendant`
FROM `Closure`
WHERE `Descendant` IN (@Query3a)
AND (`Ancestor` IN (@Query2) OR `Ancestor` = `Descendant`))
这将返回:(A, C), (B, C), (C, C)
我们现在有对所有不应取消链接的 C 父级的引用。请注意,我们没有与 F 的父级的链接。这是因为 F 没有连接到 B 和 A,除非通过 D(我们正在取消链接)。
查询 5:构建应保留的对,使用查询 3b 的结果作为查询 1 和 4 之间的桥梁
SELECT `Query4`.`ancestor`,`Query3b`.`descendant`
FROM (@Query3b) as `Query3b`
LEFT JOIN (@Query4) as `Query4`
WHERE `Query3b`.`descendant` IN (@Query1)
这将返回:(A, E), (B, E)
查询 6:运行常规查询以孤立节点及其子节点,但排除查询 5 返回的所有对
DELETE FROM `Closure`
WHERE `Descendant` IN (@Query1)
AND `Ancestor` IN (@Query2)
AND (`Ancestor`, `Descendant`) NOT IN (@Query5)
完成此操作后,我们将删除以下链接:
| Ancestor | Descendant |
-------------------------
| A | D |
| A | F |
| B | D |
| B | F |
D 和 F 都正确地取消链接,并且 E 正确地保留了与 A 和 B 的连接,留下:
A --> B --> C
|
v
D --> E < -- G
|
V
H --> F
如果我错过了什么,请告诉我!我今天自己解决了这个问题,随着时间的推移,我可能会遇到更多的边缘情况。如果我找到任何我会更新这个答案。