Sql 在具有多个父对象的闭包表中移动

Sql 在具有多个父对象的闭包表中移动,sql,directed-acyclic-graphs,transitive-closure-table,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

我有下面的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

现在我正在使用下面的查询,但它只在每个节点只有一个父节点时才起作用

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

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

(编辑:不,这不正确;不仅必须删除与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
  • F是D的后代(真)
  • E是F的父级(True)
  • E不是B(真)
  • A不是E的祖先(False)

  • 因此,
    A>>F
    不会被删除,即使它应该被删除。很抱歉,我帮不上忙,但这似乎是一个太大的问题,无法在单个查询中解决。我建议首先寻找一个算法解决方案,然后看看如何在您的案例中实现它。

    首先,我相信您的表中有一个重复的条目<代码>(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)
    ,这仍然是正确的。

    鉴于您当前的型号,我不确定这是否可行。我建议您添加一列来计算路径的数量,跟踪从任何节点
    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     |
    
    删除一个节点将需要一个update语句,后跟一个delete语句。 更新将减少该条目的引用数,而不是删除它找到的条目。然后,您可以删除引用为0或更少的条目


    现在我想不出一个进行更新的SQL查询,但从理论上讲,这应该可以在不必完全重建闭包表的情况下工作…

    重复项(a,D)实际上是这个问题的根源-它的存在表明,在闭包表中,有一个(a,D)是由ABD引起的,另一个是由ACD引起的。我如何检测另一条路径何时与此菱形相交而不删除(A,D)?最好只使用SQL,并且只给出边缘(B,D)。@NtscCobalt-在那里有两次
    (A,D)
    是没有意义的。闭包表不描述一个节点如何与另一个节点连接;只有一个节点与另一个节点连接。因此,如果
    (A,D)
    在表中,那么从
    A
    D
    的路径是否为
    A>B>C>E>…>X>Y>D
    A>D
    ,并不重要。这就是重点。闭包表省去了每次计算该路径的麻烦。如果您试图捕获用于从一个节点到另一个节点的路径,那么闭包表不是解决方案。相反,将图形存储在一个边表中并计算路径。啊,是的,我很难记住我为什么要问这个问题。我真的不记得我们决定做什么作为解决方案。我认为最初的目的是想找出一种从(B,D)中删除边的方法,并删除闭包表中由它引起的任何条目,而不会意外删除仍然存在的边。我能马上想到的唯一解决方案是从你知道只有一步之遥的节点重建整个闭包表。这是一个有趣的方法。我想知道从祖先到后代的多个引用是否具有不同的深度,如果删除其中一个引用,您将如何更新深度?