【问题标题】:Parent/Child database contains cyclical references父/子数据库包含循环引用
【发布时间】:2011-11-02 04:42:22
【问题描述】:

我有一个关键字表,其中每个关键字都分配了一个 id 并且是唯一的。我有第二个表,它将父关键字的 ID 链接到子关键字的 ID。一个关键字最多可以有大约 800 个子项或根本没有子项。孩子们可以成为更多关键字的父母(等等……)

我遇到的问题是孩子(或孙子或曾孙)可能是导致循环结构的初始关键字的父项。我正在尝试使用递归函数为初始关键字构建树数据结构,但该函数要么永远不会结束,要么超过 Python 中的 1000 级递归限制。

是否有更好的方法来设计我的父/子表以防止这种情况(或在插入期间进行预先检查),或者是否有更好的方法来编写递归函数来防止这种情况发生?我试图限制递归函数的深度,但遇到了单级问题(即子级是父级的父级)。同样,我的目标是为初始关键字创建一个树形结构。

Table Keyword:
    id int(11) not null primary key auto_increment (id of keyword)
    text varchar(255) unique (keyword text e.g. "computer help desk")

Table Keyword_Relation:
    id int(11) not null primary key auto_increment (id for parent/child combo, not keyword id)
    parent int(11) (id of parent keyword)
    child int(11) (id of child keyword)

【问题讨论】:

    标签: python mysql directed-acyclic-graphs topology


    【解决方案1】:

    您要做的是创建拓扑排序。已发布许多方法可以优化此操作,这取决于您的架构和首选方法。

    在您的情况下,听起来您没有多父母。 但是我如何以编程方式处理它是从叶节点(即没有子节点的节点)开始并上升树。 上升时,保留您遇到的节点的集合。如果你曾经重复一次遭遇,那么就会存在一个循环,并且不可能进行拓扑排序。

    您不会遇到无限循环,但您的拓扑肯定有可能拥有超过 1000 个节点...因此您可能无法使用递归。

    编辑:回答你关于“更好的设计”的问题......如果可能的话,存储根节点标识符可能是有利的。 那就是:给定父母,孩子,孙子,曾孙,曾曾孙......孙子

    每一行不仅包含它们的直接父ID,还包含根节点父ID...或一些“已知良好”的根节点

    如果你这样做,你可以通过只上升到根节点来加速拓扑排序方法,并且只包括具有相同根节点的集合。

    【讨论】:

    • 可以扩展python递归限制 --docs.python.org/library/sys.html#sys.setrecursionlimit --主要是防止无限递归。只要您知道您的拓扑具有有限数量的节点,它就应该是安全的。
    • 不幸的是,我的数据库中有多个父母。此外,例如,关键字 219 是关键字 1 的子项,但关键字 1 也是关键字 219 的子项。由于这些闭环,听起来拓扑排序是不可能的。结果树包含所有可能的孩子实际上并不重要,只是它包含一些(大)可能的孩子子集。
    • 添加“根节点”字段将打开数据库以更新异常。如果为“根节点”分配了父节点,那么所有引用该记录作为根节点的节点现在都是不正确的。尝试保持“根节点”列正确,而不是仅按需提升节点树,您将有额外的开销。
    • @DaveO 你的例子是一个循环,而不是一个多父母。多父项意味着 219 是多个其他记录的直接子项。循环正是您要防止的……
    • @Brendan 更新根节点时,还必须更新具有该根节点的所有行。这不是更新异常;这也很简单。跟踪 DAG 中的不同级别,否则需要遍历。
    【解决方案2】:

    您可以从树的顶部开始,只跟踪您已经找到的节点并忽略它们。

    def getchildren(node, ignore_nodes=[]):
        child_nodes = []
        for child in node.children():
            if child in ignore_nodes:
                continue
            child_nodes.append(child)
            ignore_nodes.append(child)
            nodes, ignore_nodes = getchildren(child, ignore_nodes)
            child_nodes.extend(nodes)
        return child_nodes, ignore_nodes
    

    【讨论】:

    • 如果他忽略它们,那么他就不能很好地检查他是否有周期。
    • @Matthew 问题问如何获取一个节点的所有子节点,即使有循环引用;这是这样做的。它忽略循环引用,防止无限递归。如果目标是首先防止循环引用(尽管我不确定这是问题所在),那么是的,这不会阻止。
    • @Brendan 我想我误解了这个问题。我读到他想检查或防止循环。我还可以看到他想要所有子节点。您的解决方案非常适合后者……而且,从 OP 的评论来看,无论如何,这似乎就是他真正需要的。 +1
    猜你喜欢
    • 2011-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-30
    • 2011-01-12
    • 1970-01-01
    • 2011-05-09
    • 1970-01-01
    相关资源
    最近更新 更多