通过PHP实现MySQL的递归树遍历

5
我正在为客户创建一个问卷,需要将问题按照三层级别进行组织。我已经成功地创建了用户界面,但是在过去的三个小时里,我一直在尝试以正确的方式从数据库中提取数据,使得所有内容都能够正确加载。由于客户组织数据库的方式,我无法控制其结构:
id    description    parentId    
1      Level 1        0           
2      Level 2        0           
3      Level 1a       1   
4      Level 1b       1 
5      Level 1a1      3      

我在网站上找到了一个与我的问题类似的问题,但当我尝试解决它时,我不断地得到以下结果:
代码:
function makeList($par_id = 0) {
    //your sql code here
    $result = mysql_query("SELECT * FROM pB_test WHERE parentId = $par_id");
    $pages = mysql_fetch_array( $result );

    if (count($pages)) {
        echo '<ul>';
        foreach ($pages as $page) {
            echo '<li>', $page['description'];
                makeList($page['parentId']);
                echo '</li>';
        }
        echo '</ul>';
    }
}

makeList();

输出:

1
3
5
5
l
l
3
5
5
l
l
3
5
5
l
l
3
5
5
l
l

有人知道如何修复这个问题以及具体是什么问题吗?谢谢。

MySQL不支持递归的JOIN,这正是你真正需要的,但是如果你知道你只有三个级别,并且永远只会有三个级别,那么你可以将表左连接到自身三次。 - DaveRandom
2个回答

8

每次调用 MySQL 服务器并获取结果并不是一个好习惯。

如果你需要查询超过100行甚至200行以上的数据呢?

可以使用以下方法只查询一次:

$result = mysql_query("SELECT * FROM test");
$arrs = array();

while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
    $arrs[] = $row;
}

function build_tree($arrs, $parent_id=0, $level=0) {
    foreach ($arrs as $arr) {
        if ($arr['parent_id'] == $parent_id) {
            echo str_repeat("-", $level)." ".$arr['name']."<br />";
            build_tree($arrs, $arr['id'], $level+1);
        }
    }
}

build_tree($arrs);

表格的常见示例

  id    name    parent_id

1
你可以在函数内部放置 global $arrs;(而不是将其作为参数传递)。这样它就不再是一个黑盒子,但你将使用更少的内存。此外,使用 mysqli 代替 mysql。 总之,我喜欢它。 - Falk
这将始终获取完整的表格,可能使用几千兆字节的内存 - 没有获取子树的可能性。 - Eugen Rieck
@EugenRieck,只需在查询中添加WHERE parent_id=$parent_id子句和第二个参数到build_tree($arrs, $parent_id)调用中。显然,这对于大数据不太适用,但是您会将千兆字节的结果包装在<ul><li>中吗? - vladkras
没错。我绝不会把几个字节包装成<ul><li>,但我会把几个太字节放进数据库里。这段错误的代码总是会获取所有行,所以对于“如果你有超过100行?或200+?”的问题,答案很简单:绝对不能用这个,因为它在任何规模的树上都会出现严重的错误。 - Eugen Rieck
2
谢谢@vladkras,到目前为止这是我找到的最好的解决方案。 - Klaujesi
显示剩余2条评论

7
请进行递归操作:
function printChildQuestions($parentid) {
  $sql="SELECT * FROM pB_test WHERE parentID=$parentid";
  $result=mysql_query($sql);
  $i=0;
  while (true) {
    $row=mysql_fetch_array($result);
    if (!$row) break;
    if ($i==0) echo "<ul>";
    $i=1;
    echo '<li>'.$row['id'].'&nbsp;'.$row['description'].'&nbsp;'.$row['parentId'].'</li>';
    printChildQuestions($row['id']);
  }
  if ($i>0) echo '</ul>';
}

printChildQuestions(0);

太棒了!非常感谢!你今天赢得了互联网的荣誉,伙计! - Keiran Lovett
那应该仍然适用,对吧?我该如何将其分割,以便每个级别都可以嵌套自己的HTML标记?如果你做不到也没关系。不过还是谢谢你的帮助! - Keiran Lovett
好的,我来试一下。谢谢伙计! - Keiran Lovett
我不明白当 $i 的值变成 > 0 时。 - Lucabro
$i和使用它的if语句可以被删除而不影响结果。 - DanAllen
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接