根据级别计算结果

时间:2012-05-15 08:58:20

标签: php mysql

我有类似的邻接列表模式结构,我想根据Food = (2,4,3), Fruit = (3,3)

等级来计算父级的所有标题

树表结构

enter image description here

之后再制作那样的树

enter image description here

通过这个代码,我得到了正确的总数,如食物= 9,水果= 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

这样的结果

但我希望得到的数据总体结果如食物2,4,3和水果3,3等级

所以plz指导如何获得总水平

6 个答案:

答案 0 :(得分: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; 
 }

让我们尝试一次..

答案 1 :(得分: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(Beef)应该有父“Meat”而不是“Beat”。

如果您想查看测试页面,请here

答案 2 :(得分:1)

这个article只需要用mysql创建一个树,以及如何逐级计数

答案 3 :(得分: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人们认为这是一个很好的解决方案吗?如果表非常大,那么一直进行表扫描和regexp会变得非常低效 - 你的方法会更好地利用任何索引,这就是为什么我说这可能不能很好地扩展,但是鉴于你没有我需要运行这么多的查询,这可能是值得一试的。

答案 4 :(得分: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());

?>

答案 5 :(得分: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 -> PepperRed会与水果Red一起陷入瘫痪。

另一件需要注意的事情是,如果您的数据库数据不是树,我的代码将进入无限循环。如果图表中有任何循环,它将永远不会完成。通过将$visited数组与已经访问过的所有节点保持在一起并且不将它们推入循环内的$next_children数组(可能使用array_diff($data[$node][1], $visited)),可以轻松解决该错误。