13

一段时间以来,我一直在思考如何最好地处理 SQL 中的层次结构。由于邻接列表的局限性和 MPTT/嵌套集的复杂性,我开始考虑简单地将关键路径存储为一个简单的node_key/node_key/...字符串。我决定整理一下这三种技术的优缺点:

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

  • 邻接 = 1
  • MPTT = 3
  • Path = 1(将旧节点路径替换为包含该路径的所有节点的新节点路径)

获取树所需的调用次数:

  • 邻接 = [子级别数]
  • MPTT = 1
  • 路径 = 1

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

  • 邻接 = [超层数]
  • MPTT = 1
  • 路径 = 0

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

  • 邻接 = [子级别数]
  • MPTT = 0(可以从右/左值计算)
  • 路径 = 1

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

  • 邻接 = [超层数]
  • MPTT = 1
  • 路径 = 0

需要的数据库字段:

  • 邻接 = 1(父)
  • MPTT = 3(父、右、左)
  • 路径 = 1(路径)

结论

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

所以问题是,存储路径不应该被认为是比 MPTT 更强大的技术吗?为什么存储路径不是一种更常用的技术,为什么您不在给定实例中通过 MPTT 使用它们?

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

更新:

以下是 MPTT 可以开箱即用的至少 2 件存储路径解决方案无法做到的事情:

  1. 允许计算每个节点的子节点计数,而无需任何额外的查询(如上所述)。
  2. 对给定级别的节点施加命令。其他解决方案是无序的。
4

2 回答 2

10

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

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

  • 关闭 = 1

获取树所需的调用:

  • 关闭 = 1

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

  • 关闭 = 1

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

  • 关闭 = 1

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

  • 关闭 = 1

需要的数据库字段:

  • 邻接 = 1 个更多字段/行
  • 路径 = 1 个更多字段/行
  • MPTT = 2 或 3 个以上字段/行
  • 闭包 = 额外表中的 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 。

于 2011-11-19T19:18:07.800 回答
3

您的结论的问题在于它忽略了使用树木所涉及的大多数问题。

By reducing the validity of a technique to the "number of calls" you effectively ignore all of the issues which well understood data structures and algorithms attempt to solve; that is, fastest execution and low memory and resource foot print.

The "number of calls" to an SQL server may seem like a good metric to use ("look ma less code"), but if the result is a program which never finishes, runs slowly, or takes up to much space, it is in fact a useless metric.

By storing the path with every node you are not creating a tree data structure. Instead you are creating a list. Any operation which a tree is designed to optimize is lost.

This might be hard to see with small date sets (and in many cases of small trees a list is better), try some examples on data sets of size 500, 1000, 10k -- You will quickly see why storing the whole path is not a good idea.

于 2011-11-19T19:18:23.367 回答