5

我有这样的邻接列表模式结构,我想根据级别计算父级的所有标题,例如Food = (2,4,3), Fruit = (3,3)

树表结构

在此处输入图像描述

之后像那样做树

在此处输入图像描述

通过这段代码,我得到了正确的总数,例如 Food = 9, Fruit = 6

function display_children($parent, $level) 
{

 $result = mysql_query('SELECT title FROM tree '.'WHERE parent="'.$parent.'"');
 $count = 0;
  while ($row = mysql_fetch_array($result))
   {
    $data=  str_repeat(' ',$level).$row['title']."\n";
    echo $data;
    $count += 1 + $this->display_children($row['title'], $level+1);
   }  
    return $count; 
 }

调用函数

 display_children(Food, 0) 

结果 : 9 // 但我想得到像 2,4,3 这样的结果

但我想根据级别获得像 For Food 2,4,3 和 For Fruit 3,3 这样的计数总结果

所以请指导如何根据级别获得总数

4

6 回答 6

3
function display_children($parent, $level) 
{

 $result = mysql_query('SELECT title FROM tree '.'WHERE parent="'.$parent.'"');
 $count = "";
  while ($row = mysql_fetch_array($result))
   {
    $data=  str_repeat(' ',$level).$row['title']."\n";
    echo $data;
    if($count!="")   
        $count .= (1 + $this->display_children($row['title'], $level+1));
    else
        $count = ", ".(1 + $this->display_children($row['title'], $level+1));
   }  
    return $count; 
 }

让我们尝试一次..

于 2012-05-16T12:28:47.930 回答
2

如果您想按级别获取金额,请让函数按级别返回它们。

function display_children($parent, $level) 
{

 $result = mysql_query('SELECT title FROM tree WHERE parent="'.$parent.'"');
 $count = array(0=>0);
  while ($row = mysql_fetch_array($result))
   {
    $data=  str_repeat(' ',$level).$row['title']."\n";
    echo $data;
    $count[0]++;
    $children= $this->display_children($row['title'], $level+1);
    $index=1;
    foreach ($children as $child)
    {
     if ($child==0)
      continue;
     if (isset($count[$index]))
      $count[$index] += $child;
     else    
      $count[$index] = $child;
     $index++;
    }
   }  
    return $count; 
 }

请注意,我很难调试代码,因为我没有你的表。如果有任何错误让我知道,我会修复它。无论如何,结果将是数组,其中应包含索引指定的级别数量:

$result=display_children("Food", 0) ;
var_export($result);//For exact info on all levels 
echo $result[0];//First level, will output 2
echo $result[1];//Second level, will output 4
echo $result[2];//Third level, will output 3

顺便说一句,您的数据库中有错字,我猜 id 10(牛肉)应该有父“肉”而不是“节拍”。

如果您想查看测试页面,请点击此处

于 2012-05-18T07:25:15.077 回答
1

本文包含了使用 mysql 创建树所需的所有内容,以及如何逐级计数

于 2012-05-21T17:18:11.773 回答
1

如果您不介意更改架构,我有一个更简单的替代解决方案。

你有你的日期在这样的表中......

item             id
-------------+------
Food         |  1
Fruit        |  1.1
Meat         |  1.2
Red Fruit    |  1.1.1
Green Fruit  |  1.1.2
Yellow Fruit |  1.1.3
Pork         |  1.2.1

查询现在要简单得多,因为它们只是简单的字符串操作。这适用于几百到几千个条目的小型列表 - 它可能无法很好地扩展 - 我没有尝试过。

但是要计算第二级有多少东西,你可以做一个正则表达式搜索。

select count(*) from items
where id regexp '^[0-9]+.[0-9]+$'

第三层只是

select count(*) from items
where id regexp '^[0-9]+.[0-9]+.[0-9]+$'

如果您只想要一个位于第 2 层的子分支

select count(*) from items
where id regexp '^[0-9]+.[0-9]+$'
and id like "1.%"

它的优点是您不需要在数据库上运行尽可能多的查询,并且作为奖励,它更容易读取表中的数据并查看发生了什么。

我有一种唠叨的感觉,这可能不被认为是“好的形式”,但它确实非常有效。我会对这种方法的任何批评非常感兴趣,DB 人认为这是一个很好的解决方案吗?如果表非常大,那么一直进行表扫描和正则表达式会变得非常低效 - 你的方法会更好地利用任何索引,这就是为什么我说这可能不能很好地扩展,但鉴于你没有不需要运行这么多查询,这可能是一个值得进行的权衡。

于 2012-05-22T01:48:11.073 回答
0

php类的解决方案:

<?php

class LevelDepCount{

    private $level_count=array();

    /**
     * Display all child of an element
     * @return int Count of element
     */
    public function display_children($parent, $level, $isStarted=true) 
    {
    if($isStarted)
            $this->level_count=array(); // Reset for new ask
     $result = mysql_query('SELECT title FROM tree '.'WHERE parent="'.$parent.'"');
     $count = 0; // For the level in the section
      while ($row = mysql_fetch_array($result))
       {
        $data=  str_repeat(' ',$level).$row['title']."\n";
        echo $data;
        $count += 1 + $this->display_children($row['title'], $level+1,false);
       }
        if(array_key_exists($level, $this->level_count))
            $this->level_count[$level]+=$count;
        else
            $this->level_count[$level]=$count;
            return $count; 
    }

    /** Return the count by level.*/
    public function getCountByLevel(){
        return $this->level_count;
    }

}

$counter=new LevelDepCount();
$counter->display_children("Food",0);
var_dump($counter->getCountByLevel());

?>
于 2012-05-19T18:21:49.950 回答
0

如果您修改查询,您可以一举获得所有数据,而无需进行太多计算(代码未经测试):

/* Get all the data in one swoop and arrange it for easy mangling later */
function populate_data() {
    $result = mysql_query('SELECT parent, COUNT(*) AS amount, GROUP_CONCAT(title) AS children FROM tree GROUP BY parent');
    $data = array();
    while ($row = mysql_fetch_assoc($result)) {
       /* Each node has the amount of children and their names */
       $data[$row['parent']] = array($row['children'], int($row['amount']));
    }
    return $data;
}

/* The function that does the whole work */
function get_children_per_level($data, $root) {
    $current_children = array($root);
    $next_children = array();
    $ret = array();

    while(!empty($current_children) && !empty($next_children)) {
        $count = 0;
        foreach ($current_children as $node) {
            $count += $data[$node][0]; /* add the amount */
            $next_children = array_merge($next_children, explode($data[$node][1])); /* and its children to the queue */
        }
        ret[] = $count;
        $current_children = $next_children;
        $next_children = array();
    }

    return $ret;
}

$data = populate_data();
get_children_per_level($data, 'Food');

修改函数以在每次调用或每个级别调用一次以填充数据结构而不将整个表放入内存中应该不难。如果你有只有几个孩子的深树,我建议不要这样做,因为一举获得所有数据并计算它会更有效。如果你有很多孩子的浅树,那么它可能值得改变。

也可以将所有内容放在一个函数中,但我会避免在不需要时为重复调用重新计算数据。一个可能的解决方案是使它成为一个类,将populate_data函数用作将其存储为内部私有属性的构造函数和一个与get_children_per_level没有第一个参数相同的方法,因为它将从其内部私有中获取数据财产。

无论如何,我还建议您将 ID 列用作“父”引用而不是其他列。首先,如果任何名称包含逗号:P,我的代码将中断。此外,您可能有两个具有相同名称的不同元素。例如,您可以拥有Vegetables -> Red -> Pepper,并且Red将与 Fruit's 一起下滑Red

另外需要注意的是,如果您的数据库数据不是树,我的代码将进入无限循环。如果图中有任何循环,它将永远不会结束。通过保留一个$visited包含所有已访问节点的数组而不将它们推入$next_children循环内的数组(可能使用array_diff($data[$node][1], $visited).

于 2012-05-22T04:07:25.893 回答