树遍历递归计算

时间:2012-07-16 20:34:44

标签: php mysql algorithm tree

我们拥有用户,问题和无限级别的类别。用户可以从问题中获得一些积分。问题可以有多个类别。

我想要做的是计算每个类别的最高用户数:它只是从该类别下的问题中获取的总点数,也是它的子类别。

所以,我有这些表:

questions
--------------
id
title
question

categories
--------------
id
parent_id
category
lft
rgt

question_categories
--------------
question_id
category_id

users
--------------
id
username

user_points
--------------
id
user_id
question_id
point_type
points

user_category
--------------
user_id
category_id
points    

我想要做的是计算user_category.points值。 总结每个类别的分数很容易,但包括子类别变得越来越复杂。

这可能是最好的方法吗?

示例计算:

我们说类别是:

Programming
   PHP
      Zend Framework
      Symfony
   Java
   Ruby on Rails

假设用户从Zend Framework获得 3分,从PHP获得 2分,从java获得 5分 1分来自Rails。每个类别的此用户的积分将为:

Programming            11 (5+5+1)
   PHP                  5 (2+3)
      Zend Framework    3
      Symfony
   Java                 5
   Ruby on Rails        1

4 个答案:

答案 0 :(得分:1)

也许最好使用标签而不是层次结构。例如,任何带有" Zend Framework"还将拥有" PHP"和"编程"标签。当某些类别可以出现在多个位置时,这也会有所帮助。例如,我可以在jQuery和Javascript中使用ajax。然后,为用户的类别中列出的每个标记添加1。

答案 1 :(得分:0)

我会创建一个user_categories表格,在其中我会存储3个值:user_idcategory_iduser_score。它易于维护(只需要INSERTUPDATE),并且查询每个类别的最高用户也很容易。

答案 2 :(得分:0)

如果您只是按每个顶级类别求和,那么您应该在您的类别表中添加一个名为root_id的字段(保存该类别的传递父类的ID)。

然后您的总和将计算为:

select up.user_id, ctg.root_id, sum(up.points)
from user_points up
join question_categories qc on up.question_id = qc.question_id
join categories ctg on qc.category_id = ctg.id
group by up.user_id, ctg.root_id

答案 3 :(得分:0)

这个php和SQL应该让你获得每个类别的前3个用户,包括子类别:

$query = "SELECT id, parent_id FROM categories";
$parent = array();
...fetch mysql data loop depending on what connection you use, mysqli or pdo...
{
   $parent[$result['id']] = $result['parent_id'];
}

$childs = array();

foreach($parent as $id => $parrent_id)
{
   $childs[$parrent_id][$id] = $id;
   $next_parrent_id = $parrent_id;
   while($next_parrent_id = $parent[$next_parrent_id])
   {
      $childs[$next_parrent_id][$id] = $id;
   }
}

foreach($parent as $id => $parrent_id)
{
   $current_categories = array($id => $id) + $childs[$id];
   $query = "SELECT user_id, username, SUM(points) AS total_points
             FROM user_points 
             LEFT JOIN users ON (user_id = users.id)
             LEFT JOIN question_categories USING (question_id)
             WHERE category_id IN (" . implode(', ', $current_categories). ")
             ORDER BY total_points DESC
             LIMIT 3";
   ...fetch mysql data loop...
}