在数据库中实现分层数据结构

时间:2009-02-13 03:48:34

标签: mysql database hierarchical-data

我知道有两种方法:邻接列表和嵌套树。据说由于大量查询,邻接列表在遍历上使用会很慢。但我不知道这方面的任何实际数字。我正在制作的网站将有200页。遍历生成(例如)站点地图需要花费超过0.3秒的时间吗?

使用LAMP堆栈在MySQL(innoDB)上运行。

如果可能的话,我更愿意实现邻接,因为设计更加简单。

感谢。

6 个答案:

答案 0 :(得分:14)

除了你提到的两个选项之外,还有更多的选择。有:

  • 邻接列表(几乎每个人都使用的“parent_id”)
  • 嵌套集
  • 路径枚举
  • 关闭表(又称邻接关系)

请参阅我对“What is the most efficient/elegant way to parse a flat table into a tree?

的回答

或者几本书:

答案 1 :(得分:4)

文章Managing Hierarchical Data in MySQL详细介绍了这一点。

我建议使用“嵌套集”技术,因为它允许您在一个查询中获取整个树(及其子节点)。基本上读取是便宜的,但写入是昂贵的,因为整个树必须重新平衡。但是如果你有99%的读数,那么它是完全合理的。

答案 2 :(得分:3)

解析邻接列表的天真方法需要大量查询,而对于大型列表,可能需要花费大量时间来构建内存。作为参考,我所指的天真方法可以概括为:选择没有父项的所有项目,然后为每个项目递归地获取它的子项。这种方法需要n + 1个数据库查询。

我使用以下方法构建一个带有1个查询的邻接列表。从数据库中选择所有项目。将所有项目转移到由其键索引的数组中。遍历数组并将父对象的引用分配给每个子对象。第二次遍历数组并删除仅留下根级对象的所有子对象。

由于您提到了LAMP堆栈,因此执行此操作的PHP代码大致如下:

<?php
// Assumes $src is the array if items from the database.
$tmp = array();

// Traverse the array and index it by id, ensuing each item has an empty array of children.
foreach ($src as $item) {
  $item['children'] = array();
  $tmp[$item['id']] = $item;
}

// Now traverse the array a second time and link children to their parents.
foreach ($tmp as $id => $item) {
  if ($item['parent_id'] != 0 || $item['parent_id'] !== NULL) {
    $tmp[$item['parent_id']]['children'][$id] = &$tmp[$id];
  }
}

// Finally create an array with just root level items.
$tree = array();
foreach ($tmp as $id => $item) {
  if ($item['parent_id'] == 0 || $item['parent_id'] === NULL) {
    $tree[$id] = $item;
  }
}

// $tree now contains our adjacency list in tree form.
?>

请注意,此代码旨在说明从单个数据库查询构建邻接列表的技术。它可能可以针对更少的内存消耗等进行优化。它还没有经过测试。

吉姆,

答案 3 :(得分:2)

答案 4 :(得分:2)

另一种方法称为“嵌套集”,我认为不是“嵌套树”。

无论如何,站点地图的一个好处是你可能知道它的最大深度。我认为邻接模型的问题是相应的SQL一次只能在一个层面上工作,所以如果你有'n'个级别,那么你需要一个'n'个SQL语句的循环...但我认为(我'我不确定)如果您事先知道最大'n',那么您可以编写相应的固定数量级别的SQL。

0.3秒对我来说听起来很长时间才能看到200页,所以这很可能。

网站地图也不经常更新;因此,即使从SQL检索确实需要很长时间,您也可以将检索/计算的树缓存在RAM中。

或者,不要担心构建树的SQL,您可以尽可能简单地存储它(作为邻接列表),将其作为一组简单的行从数据库中检索,并在RAM中构建树(在高级编程语言中使用循环)而不是使用SQL中的循环来使用SQL语句构建树。

答案 5 :(得分:2)

完整性:Oracle有START_WITHCONNECT_BY个运算符:请参阅此Hierarchical Queries文档。