具有单个查询的递归类别?

时间:2010-06-25 07:39:48

标签: php mysql

我有一个包含文章和部分的网站, 每个部分都可以有一个父部分,尽可能多 例如:

subject 1
 -subject 2 
 --subject 3
 -subject 4
 --subject 5
 --subject 6
 ---subject 7
subject 8
subject 9

等。

现在,我想以递归方式获取它们,通过php和mysql执行此操作的最有效方法是什么?

Tnx in advanced。

11 个答案:

答案 0 :(得分:27)

如果树不是太大,你可以使用一些聪明的引用在PHP中构建树。

$nodeList = array();
$tree     = array();

$query = mysql_query("SELECT category_id, name, parent FROM categories ORDER BY parent");
while($row = mysql_fetch_assoc($query)){
    $nodeList[$row['category_id']] = array_merge($row, array('children' => array()));
}
mysql_free_result($query);

foreach ($nodeList as $nodeId => &$node) {
    if (!$node['parent'] || !array_key_exists($node['parent'], $nodeList)) {
        $tree[] = &$node;
    } else {
        $nodeList[$node['parent']]['children'][] = &$node;
    }
}
unset($node);
unset($nodeList);

这将为您提供$tree中的树结构,其子项位于相应的children - 插槽中。

我们使用相当大的树(>> 1000项)完成了这项工作,它非常稳定,比在MySQL中执行递归查询要快得多。

答案 1 :(得分:10)

这取决于您存储数据的方式。 MySQL.com上有一篇名为Managing Hierarchical Data in MySQL的好文章,讨论了这个问题。

答案 2 :(得分:3)

嗯,您可以在一个查询中获取数组中的所有类别,如您所知:

$query = "SELECT `name`,`id` from `table`";

在数组中使用它,可以使用一些嵌套循环构建树。它不会很快,但比使用递归查询更简单,更快。此外,您可以缓存构建的树,而不必每次都重建它。

答案 3 :(得分:2)

你可以看看这个话题:昨天开放的how to get the hierarchical menu from mysql并且大致相同。

答案 4 :(得分:2)

Mine也使用一个查询递归...

A recursive method to storing hierarchical data without multiple calls to the database

我也希望使用递归,因为它简单但像我一样,我想删除递归查询的开销。我的逻辑以这种方式说你只是将负载从数据库移动到内存,具体取决于数据库结果的大小,但我还没有对此进行任何真正的可伸缩性测试,所以我不知道它有多大的影响vs非递归方法。

答案 5 :(得分:1)

我不能保证我没有犯任何语法错误但这应该适用于一个查询。

class menuSystem{ 
var $menu;
var $db; #this variable is my db class assigned from the construct, I havent written the construct in, I can if you need it
function startNav(){
    $this->db->runQuery("select * from table order by parent asc");
    $menu = array(0 => array('children' => array()));
    while ($data = $this->db->fetchArray()) {
      $menu[$data['category_id']] = $data;
      $menu[(is_null($data['parent']) ? '0' : $data['parent'] )]['children'][] = $data['category_id'];
    }
    $this->menu = $menu;
    $nav = '<ul>';
    foreach($menu[0]['children'] as $child_id) {
      $nav .= $this->makeNav($menu[$child_id]);
    }
    $nav .= '</ul>';
}

function makeNav($menu){
   $nav_one = '<li>'."\n\t".'<a href="#">'$menu['name'].'</a>';
    if(isset($menu['children']) && !empty($menu['children'])) {
      $nav_one .= "<ul>\n";
      foreach($menu['children'] as $child_id) {
        $nav_one .= $this->makeNav($this->menu[$child_id]);
      }
      $nav_one .= "</ul>\n";
    }
    $nav_one .= "</li>\n";
return $nav_one;
}

}

编辑:抱歉,我在我的代码中使用它作为一个类,并认为我已经设法将它从课程中取出但是忘了我需要$ this-&gt;菜单

更新:我认为现在下面的课程已经不合时宜了,对不起这么长的答案

$result = mysql_query("select * from table order by parent_id asc");
$menu = array(0 => array('children' => array()));
while ($data = mysql_fetch_array($result)) {
  $menu[$data['category_id']] = $data;
  $menu[(is_null($data['parent_id']) ? '0' : $data['parent_id'] )]['children'][] = $data['category_id'];
}
$global_menu = $menu;
$nav = '<ul>';
foreach($menu[0]['children'] as $child_id) {
  $nav .= makeNav($menu[$child_id]);
}
$nav .= '</ul>';

function makeNav($menu) {
  global $global_menu;
  $nav_one = '<li>'."\n\t".'<a href="#">' . $menu['name'].'</a>';
  if(isset($menu['children']) && !empty($menu['children'])) {
    $nav_one .= "<ul>\n";
    foreach($menu['children'] as $child_id) {
      $nav_one .= makeNav($global_menu[$child_id]);
    }
    $nav_one .= "</ul>\n";
  }
  $nav_one .= "</li>\n";
  return $nav_one;
}

希望有所帮助

答案 6 :(得分:0)

文章的第一部分仅涉及4个级别,最后一部分不是我想要的方式。

我的结构是这样的:

+-------------+----------------------+--------+
| category_id | name                 | parent |
+-------------+----------------------+--------+
|           1 | test                 |   NULL |
|           2 | subject1             |   1    |
|           3 | subject2             |   1    |
|           4 | subject3             |   2    |
|           5 | subject4             |   4    |
+-------------+----------------------+--------+

我不想让事情复杂化,我想以最简单的方式做到这一点,但要以最有效的方式获取数据。

答案 7 :(得分:0)

如果假设您的表格有 id id_parrent 和名称字段

function tree($id)
{
    $query = "SELECT `name`,`id` from `table` WHERE `id_parrent` = '$id'";
    $result = mysql_query($query);
     if(mysql_num_rows($result) != 0)
       {
            echo "<ul>";
            while($row = mysql_fetch_array($result))
            {             
                 echo "<li>",$row[name],"</li>";
                 tree($row[id]);
            }
            echo "</ul>";
       }
}

所以你会得到整棵树

category1
       category1_1
       category1_2
              category1_2_1
              category1_2_2
       category1_3
...........................

答案 8 :(得分:0)

从您的示例到每个类别,在另一个字段中保存其完整路径:
1 - 1
2 - 1.2
3 - 1.2.3
4 - 1.4
5 - 1.4.5
6 - 1.4.6
7 - 1.4.6.7
8 - 8
9 - 9

然后只在该字段上使用ORDER BY进行查询

答案 9 :(得分:0)

我对这个问题有一个很好的解决方案。

不使用递归。它需要单个查询到数据库。

我刚刚在这里回答了类似问题的答案:

https://stackoverflow.com/questions/2871861#3368622

感谢。

答案 10 :(得分:0)

我已经为您查询过了。这将为您提供单个查询的递归类别:

SELECT id,NAME,'' AS subName,'' AS subsubName,'' AS subsubsubName FROM app_category WHERE Parent_id=0
UNION 
SELECT b.id,a.name,b.name AS subName,'' AS subsubName,'' AS subsubsubName FROM app_category AS a LEFT JOIN app_category AS b ON b.parent_id=a.id WHERE a.Parent_id=0 AND b.name IS NOT NULL 
UNION 
SELECT c.id,a.name,b.name AS subName,c.name AS subsubName,'' AS subsubsubName FROM app_category AS a LEFT JOIN app_category AS b ON b.parent_id=a.id LEFT JOIN app_category AS c ON c.parent_id=b.id WHERE a.Parent_id=0 AND c.name IS NOT NULL 
UNION 
SELECT d.id,a.name,b.name AS subName,c.name AS subsubName,d.name AS subsubsubName FROM app_category AS a LEFT JOIN app_category AS b ON b.parent_id=a.id LEFT JOIN app_category AS c ON c.parent_id=b.id LEFT JOIN app_category AS d ON d.parent_id=c.id WHERE a.Parent_id=0 AND d.name IS NOT NULL 
ORDER BY NAME,subName,subsubName,subsubsubName 

这是fiddle