首页 文章

在SQL中管理层次结构:MPTT /嵌套集与邻接列表与存储路径

提问于
浏览
11

有一段时间我一直在努力解决如何最好地处理SQL中的层次结构 . 由于邻接列表的限制和MPTT /嵌套集的复杂性而感到沮丧,我开始考虑简单地存储密钥路径,作为一个简单的 node_key/node_key/... 字符串 . 我决定编译这三种技术的优点和缺点:

创建/删除/移动节点所需的呼叫数:

  • 邻接= 1

  • MPTT = 3

  • Path = 1(用包含该路径的所有节点的新节点路径替换旧节点路径)

获取树所需的调用次数:

  • Adjacency = [子级数]

  • MPTT = 1

  • 路径= 1

获取节点/祖先路径所需的调用次数:

  • 邻接= [超级数量]

  • MPTT = 1

  • 路径= 0

获取子节点数所需的调用次数:

  • Adjacency = [子级数]

  • MPTT = 0(可以从右/左值计算)

  • 路径= 1

获取节点深度所需的调用次数:

  • 邻接= [超级数量]

  • MPTT = 1

  • 路径= 0

需要

个DB字段:

  • 邻接= 1(父)

  • MPTT = 3(父,右,左)

  • 路径= 1(路径)

结论

除了一个用例之外,存储的路径技术使用与其他技术相同或更少的调用 . 通过这种分析,存储路径是明显的赢家 . 更不用说,它实现起来要简单得多,人类可读等等 .

所以问题是,不应该将存储路径视为比MPTT更强大的技术吗?为什么存储路径不是更常用的技术,为什么不在给定实例中使用它们而不是MPTT?

另外,如果您认为此分析不完整,请告诉我 .

更新:

这里至少有两件事MPTT可以开箱即用,存储的路径解决方案不会:

  • 允许计算每个节点的子节点数,而无需任何其他查询(如上所述) .

  • 在给定级别的节点上强加订单 . 其他解决方案是无序的 .

2 回答

  • 9

    您也可以考虑我在What is the most efficient/elegant way to parse a flat table into a tree?的回答中描述的闭包表设计

    创建/删除/移动节点所需的调用:

    • 关闭= 1

    获取树所需的调用:

    • 关闭= 1

    获取节点/祖先路径所需的调用:

    • 关闭= 1

    获取子节点数所需的调用:

    • 关闭= 1

    获取节点深度所需的调用:

    • 关闭= 1

    需要

    个DB字段:

    • Adjancency =另外1个字段/行

    • Path =另外1个字段/行

    • MPTT = 2或3个字段/行

    • Closure =额外表格中的2或3个字段 . 该表的最坏情况为O(n ^ 2)行,但远少于大多数实际情况 .

    还有其他一些注意事项:

    支持无限深度:

    • 邻接=是

    • MPTT =是

    • 路径=没有

    • 关闭=是的

    支持参照完整性:

    • 邻接=是

    • MPTT =没有

    • 路径=没有

    • 关闭=是的

    我还在我的演示文稿Models for Hierarchical Data with SQL and PHP和我的书SQL Antipatterns: Avoiding the Pitfalls of Database Programming中介绍了Closure Table .

  • 3

    你得出的结论是,它忽略了使用树木时涉及的大多数问题 .

    通过将技术的有效性降低到“调用次数”,您可以有效地忽略所有理解数据结构和算法试图解决的问题;也就是说,执行速度最快,内存和资源占用量低 .

    SQL服务器的“调用次数”似乎是一个很好的度量标准(“少看代码”),但如果结果是一个永远不会完成,运行缓慢或占用太多空间的程序,那么实际上是一个无用的指标 .

    通过存储每个节点的路径,您不会创建树数据结构 . 相反,您正在创建一个列表 . 树被设计为优化的任何操作都将丢失 .

    使用小日期集可能很难看到(在很多小树的情况下,列表更好),尝试一些大小为500,1000,10k的数据集的示例 - 您将很快看到为什么存储整个路径不是一个好主意 .

相关问题