【问题标题】:Implementing a hierarchical data structure in a database在数据库中实现分层数据结构
【发布时间】:2009-02-13 03:48:34
【问题描述】:

我知道有两种方法:邻接表和嵌套树。据说由于大量查询,邻接表在遍历时会变得很慢。但我不知道任何现实的数字。我正在制作的网站将有 200 页左右。遍历生成(例如)站点地图的时间是否会超过 0.3 秒?

在带有 LAMP 堆栈的 MySQL (innoDB) 上运行。

如果可能的话,我更喜欢实现邻接,因为它的设计更简单。

谢谢。

【问题讨论】:

  • 所以基本上,你的问题是,如果某些数据结构的未知实现在未知硬件上运行需要不到 0.3 秒?不错。
  • @Shy - LAMP 堆栈上的 MySQL innoDB 数据库。
  • 组装一个原型并进行一些台架测试应该不难。什么 RDBMS 将托管这个?
  • 不认真,哪个 DBMS,而不是哪个想要的 DBMS? :-)
  • @Pax - 我们也在工作中使用 Oracle,但我选择 MySQL,尽管我承认功能少得多。 Oracle在升级的时候出现了很多问题,就像80年代的一些软件一样。

标签: mysql database hierarchical-data


【解决方案1】:

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

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

查看我对“What is the most efficient/elegant way to parse a flat table into a tree?”的回答

或者几本书:

【讨论】:

  • 谢谢,非常全面。
【解决方案2】:

文章Managing Hierarchical Data in MySQL对此进行了详细介绍。

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

【讨论】:

    【解决方案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.
    ?>
    

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

    吉姆,

    【讨论】:

      【解决方案4】:

      【讨论】:

      • 不是我的专业领域,但我确实摆了一个卑鄙的搜索框。
      【解决方案5】:

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

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

      在我看来 0.3 秒对于计算 200 页来说是一个很长的时间,所以这可能没问题。

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

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

      【讨论】:

      • 谢谢。我现在知道'n'个级别(4),但它可能会改变。我认为也许只实现嵌套集而不是更简单的方法是值得的。
      • 我认为 SQL 语句会比 PHP 中的循环快很多。
      • 我不懂 PHP。嵌套集的好处是您可以使用一个 SELECT 检索整个分支(而不是整个树)。也许这是唯一的好处,除非您需要这样做(为什么要这样做?),否则不值得额外的复杂性。
      • 如果你只有一棵只包含 200 页的树,那么获取整个数据集并找出应用程序代码中的层次结构并不是什么大问题。如果您有数十万个节点或数千个并行树,那就不同了。
      【解决方案6】:

      为了完整性:Oracle 有 START_WITH 和 CONNECT_BY 运算符:请参阅此 Hierarchical Queries 文档。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-03-12
        • 2012-08-11
        • 1970-01-01
        • 1970-01-01
        • 2010-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多