【问题标题】:Organizing mySQL data using recursive PHP使用递归 PHP 组织 mySQL 数据
【发布时间】:2012-06-15 01:43:48
【问题描述】:

我正在为一位客户创建一份问卷,要求按 3 层级别组织问题。我已经成功创建了 U.I.但是,在过去的 3 个小时里,我一直在尝试从数据库中提取数据,以使所有内容都加载到正确的位置。数据库是由客户端组织的,所以我无法控制它:

id    description    parentId    about
1      Level 1        0           This is the top level or in my case tab1
2      Level 2        0           This is the next tab in the top level
3      Level 1a       1           This is the first category under tab1
4      Level 1b       1           This is the next category under tab1
5      Level 1a1      3           This is the content under the first category of tab1

因此,任何 parentId 为 0 的内容都是顶层,并且将包含 parentId 为 1 的第二级的任何内容,依此类推。令人困惑的是,我几乎无法理解这一点,但这是我被告知这样做的方式。

什么方法是执行此类操作的最佳方式?下面附上了我用作参考的另一个问题的示例(尽管不起作用)

foreach (mysql_query("SELECT * FROM pB_test ORDER BY id ASC") as $row) {
  $menuitem = array_merge(array(), $row);
  $menuLookup[$menuitem['id']] = $menuitem;
  if ($menuitem['parent'] == null) {
    $menuitem['path'] = "/" . $menuitem['name'];
    $menu[] = $menuitem[];
  } else {
    $parent = $menuLookup[$menuitem['parent']];
    $menuitem['path'] = $parent['path'] . "/" . $menuitem['name'];
    $parent['menu'][] = $menuitem;
  }
}

任何帮助将不胜感激。干杯

【问题讨论】:

    标签: php mysql recursion if-statement adjacency-list


    【解决方案1】:

    如果您使用的是外部数据库,则可能值得在 PHP 中执行此操作,而不是 SQL。我没有对以下内容进行基准测试,因此请尝试使用您的数据,看看性能是否有问题。

    您可以自行选择如何处理孤立记录(引用不再存在的 parentID)。

    像这样在 PHP 中进行排序要求您事先拥有所有数据,因此请使用 PDO 的 fetchAll(PDO::FETCH_ASSOC) 方法之类的东西,这应该会产生如下结果:

    $data_from_database = array(
        array("id" => 1, "parentId" => 0, "description" => "Level 1"),
        array("id" => 2, "parentId" => 1, "description" => "Level 1a"),
        array("id" => 3, "parentId" => 1, "description" => "Level 1b"),
        array("id" => 4, "parentId" => 0, "description" => "Level 2"),
        array("id" => 5, "parentId" => 2, "description" => "Level 1a1"),
        array("id" => 6, "parentId" => 5, "description" => "Level 1a11a"),
        array("id" => 7, "parentId" => 5, "description" => "Level 1a11b"),
        array("id" => 8, "parentId" => 9, "description" => "Level 3"),
    );
    

    首先,您需要将主键 (ID) 作为数组的键。下面还将键“children”和“is_orphan”添加到每条记录。

    $data_by_id = array();
    foreach($data_from_database as $row)
        $data_by_id[$row["id"]] = $row + array(
            "children" => array(), 
            "is_orphan" => false
        );
    

    这看起来像这样:

    $data_from_database = array(
        1 => array("id" => 1, "parentId" => 0, "description" => "Level 1", 
                   "children" => array(), "is_orphan" => false),
        ...
     );
    

    现在,它变得很棘手:我们将遍历数组并添加引用。

    foreach($data_by_id as &$row)
    {
        if($row["parentId"] > 0)
        {
            if(isset($data_by_id[$row["parentId"]]))
                $data_by_id[$row["parentId"]]["children"][] = &$row;
            else
                $row["is_orphan"] = true;
        }
    }
    unset($row); // Clear reference (important).
    

    最后一步是清理数组的“根”。它将包含对重复行的引用。

    foreach($data_by_id as $id => $row)
    {
        // If you use this option, you'll remove
        // orphaned records.
        #if($row["parentId"] > 0)
        #    unset($data_by_id[$id]);
    
        // Use this to keep orphans:
        if($row["parentId"] > 0 AND !$row["is_orphan"])
            unset($data_by_id[$id]);
    }
    

    在每一步之后使用print_r($data_by_id) 看看会发生什么。

    如果这被证明是一项耗时的操作,请尝试通过仅执行SELECT id, parentId FROM ... 来构建树,然后稍后获取元数据,例如description。您还可以将结果存储在 Memcache 中或序列化到数据库中。

    【讨论】:

      【解决方案2】:

      我也遇到了同样的问题,但是经过大量的谷歌搜索和 stackoverflowing :-) 我找到了我的答案...... 这是我的编码方式。

      function showComments($parent = 0)
      {
      $commentQuery = "SELECT * FROM comment WHERE parent = ".mysql_real_escape_string($parentId);
      $commentResult = mysql_query($commentQuery)
      
      while ($row = mysql_fetch_array($commentResult))
      {
      echo '[Table containing comment data]';
      showComments($row['commentID']);
      }
      }
      
      showComments();
      

      【讨论】:

        【解决方案3】:

        如果你正好有 3 个关卡,那么你可以试试这个:

        http://sqlfiddle.com/#!2/70e96/16

        (
          SELECT 1 AS lvl,
                 top_level.description AS o1, top_level.id AS id1,
                                  NULL AS o2,         NULL AS id2,
                                  NULL AS o3,         NULL AS id3,
                 top_level.*
          FROM   node AS top_level
          WHERE  top_level.parentId = 0
        )UNION ALL(
          SELECT 2 AS lvl,
                 top_level.description      AS o1, top_level.id      AS id1,
                 category_level.description AS o2, category_level.id AS id2,
                                       NULL AS o3,              NULL AS id3,
                 category_level.*
          FROM       node AS top_level
          INNER JOIN node AS category_level ON category_level.parentId = top_level.id
          WHERE      top_level.parentId = 0
        )UNION ALL(
          SELECT 3 AS lvl,
                 top_level.description      AS o1, top_level.id      AS id1,
                 category_level.description AS o2, category_level.id AS id2,
                 last_level.description     AS o3, last_level.id     AS id3,
                 last_level.*
          FROM       node AS top_level
          INNER JOIN node AS category_level ON category_level.parentId = top_level.id
          INNER JOIN node AS last_level ON last_level.parentId = category_level.id
          WHERE      top_level.parentId = 0
        )
        ORDER BY o1,o2,o3;
        

        我在选择中添加了一个lvl 字段,每个级别都有不同的值。还添加了 o1,o2,o3 以便很好地排序嵌套级别,当然您可能还有其他需求。您可以在 PHP 中处理所有行,例如将它们拆分为 3 个数组(每个级别一个),或者可以通过 id 创建一个查找表等。

        【讨论】:

        • 对不起,如果这是一个无知的问题。但是我将如何在 php 中组织它?
        • biziclop 的想法不错。你有三个层次。使用查询来获取所有记录,然后对它们运行一个循环,检查parentId 并分配给一个数组。我认为没有其他办法。嗯,有 - 使用查询来获取顶级记录,然后循环并为每个顶级记录找到它的孩子,然后对于每个孩子,你需要一个查询来找到它的孩子,等等。
        • 问题是我无法控制数据库。所以一切都必须通过我身边的php来完成。我已经做了一些查找和递归的 php 树遍历似乎是这样做的方法。虽然 o.o 没有任何效果
        • 除了我回答中查询返回的字段之外,您还需要处理什么数据?
        • 更新了我的问题以进一步反映我的理解并使我的观点更加清晰
        猜你喜欢
        • 2014-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多