1

我正在为客户创建一份问卷,要求将问题按 3 层级别组织。我已经成功创建了 UI,但是在过去的 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;
  }
}

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

4

3 回答 3

1

如果你正好有 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 创建一个查找表等。

于 2012-06-12T08:35:56.960 回答
0

我也有同样的问题,但经过大量的谷歌搜索和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();
于 2012-12-11T11:06:47.203 回答
0

如果您正在使用外部数据库,那么在 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 中或序列化到数据库中。

于 2012-12-11T12:21:41.490 回答