在关系型数据库中,存储树形结构的已知方法有哪些?

56

有一个"将外键放到父级"的方法,即每个记录指向其父级。
这对于阅读操作来说很困难,但非常易于维护。

然后还有一个"目录结构键"方法:

0001.0000.0000.0000 main branch 1
0001.0001.0000.0000 child of main branch one
etc

这种写法很容易阅读,但难以维护。
还有哪些方式以及它们的优缺点?


我们正在使用老式的通过外键引用父级来存储分层数据的方法,并且几乎感到满意。为了加载大量数据,我们会进行XML查询并将其反序列化为对象。 - Kangkan
1
乍一看,树形结构和关系型数据库是非常不匹配的。如果我曾经见过使用结构化存储的用例,那么这就是它了。 - msw
2
+1,那个“可怕的适配”链接提供了使用2个结构的示例和一些建议阅读材料。 - phkahler
2
请参考https://dev59.com/wXRA5IYBdhLWcg3wvQhh,了解关于嵌套集、键命名方案以及其他将层次结构压缩到关系中的方法,适用于实例化线程评论等。 - Constantin
@Constantin这不就是我在这里展示的第二种方式吗? - Itay Moav -Malimovka
“最佳” 实现取决于数据结构和读写特性。如需查阅一个几乎全面由社区更新的选项列表,以便确定哪种实现最适合您,请访问:https://dev59.com/hW855IYBdhLWcg3w_5cQ - Kache
4个回答

66
  • Retrieving a whole tree and finding all parents is easy and cheap

  • Inserting and updating is relatively cheap

  • No recursion needed in SQL, so it's faster than the Naive Approach

  • Can handle non-hierarchical relationships as well

  • Cons:

    • Requires an additional table to store the closure information

    • More complex to implement compared to other solutions

  • 易于实现

  • 查找所有父节点成本低廉

  • 插入成本低廉

  • 检索整个树的成本低廉

  • 缺点:

    • 需要额外的表格

    • 与其他方法相比占用大量空间

    • 移动子树成本高昂

    我更喜欢最后两种方法之一,具体取决于数据变化的频率。

    另请参阅:http://media.pragprog.com/titles/bksqla/trees.pdf


    我同意你的观点:其实并没有最好的解决方案。不过,有几种不同的方法来表示树形数据结构,并且有几种技术可以将这些表示方式适应关系模型。通常,我更喜欢区分两种情况:1. 读密集型和2. 写密集型。根据具体情况,一个模型比另一个更适合:例如,如果是写密集型,向非规范化表中写入大量指针可能会非常昂贵,从而降低性能。 - Paolo Maresca
    对于第三个选项,请使用您最喜欢的搜索引擎搜索“Materialized Path”以了解更多信息。我花了一段时间才找到它的名称。第二个选项也称为“嵌套集合”。 - Fredrik Boström

    22

    修改后的先序遍历树

    这是一种使用非递归函数(通常是单行SQL语句)从数据库中检索树的方法,代价是更新稍微复杂一些。

    显示带编号的分层树的图解

    请参阅Sitepoint文章“在数据库中存储分层数据”的第2节,以获取更多详细信息。


    2
    这篇文章方便地忽略了树后续可能需要更新,因此需要重新编号大量节点以保持一致性的问题。 - Lasse V. Karlsen
    6
    这篇文章完全没有忽略这一点。它明确在第3页上说,你必须更新节点编号,并给出了相应的SQL语句。 - siride
    2
    需要注意的是,当您具有比写入更多的读取时,此模型非常出色-如果您期望进行大量写入(例如每4次读取1次写入),则此模型可能存在问题,使用简单的父ID(和更昂贵的读取)可能更可取。 - mindplay.dk
    “修改过的前序树遍历”是解决这个问题的一种优美方式,但前提是不经常修改树结构。 - Bin

    5
    我认为存储分层数据结构的“黄金方式”是使用分层数据库,例如HDB。这是一个处理树状结构非常好的关系型数据库。如果您需要更强大的功能,LDAP可能适合您。
    SQL数据库不适合处理此抽象拓扑结构。

    我知道我的限制,现在我必须使用 MySql,所以我需要在这些限制条件下找到一个好的解决方案。 - Itay Moav -Malimovka
    @msw 这就是我在问题中提到的FK方法。 - Itay Moav -Malimovka

    0

    我认为使用关系型数据库构建树形结构并不难。

    然而,面向对象的数据库对于这个目的会更加有效。

    使用面向对象的数据库:

    parent has a set of child1  
    child1 has a set of child2  
    child2 has a set of child3  
    ...  
    ...
    

    在面向对象的数据库中,你可以相当容易地构建这种结构。
    在关系型数据库中,你需要维护到父表的外键。
    parent  
    id  
    name  
    
    child1  
    parent_fk  
    id  
    name 
    
    child2  
    parent_fk  
    id  
    name  
    
    ..
    

    基本上,在构建树形结构时,您将不得不连接所有这些表,或者您可以对它们进行迭代。

    foreach(parent in parents){
       foreach(child1 in parent.child1s)
        foreach(child2 in child1.child2s)
    
    ...
    

    以这种方式迭代数据库可能非常昂贵,特别是当您需要从大型树结构中检索子树时。 - GergelyPolonkai

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接