2011-05-19 55 views
4

我想统计在使用邻接模型(父 - 子键)的表中维护的任何级别的树结构下的所有子节点的数量。表结构和数据看起来像这样:统计表中分层数据的所有子节点

id - item- parentid  
1 - A - 
2 - B - 1 
3 - C - 1 
4 - D - 2 
5 - E - 2 
6 - F - 3 
7 - G - 3 
8 - H - 5 
9 - I - 5 
10 - J - 9 
11 - K - 4 

例如B已经以下子和大子结构:

    • Ë
      • ħ
        • Ĵ
    • ˚F
      • ķ

现在,如果要统计 “B的所有子节点的” 我的答案应该是6

任何基于纯SQL查询的解决方案都会非常有帮助。或者mysql/php也可以。

谢谢!

+0

有许多不同的方法来做到这一点,我已经张贴解答http://stackoverflow.com/questions/5995823/storing-hierarchical-data-mysql - 用于转诊营销。为了确定哪些方法是有用的,我们需要知道您希望从此表获得什么类型的信息,以及表更新的频率 - 信息添加和删除的频率以及应该如何处理如果某些东西从层次结构中间被删除,则会发生。你能详细说明你想要对这个层次结构做什么吗? – 2011-05-19 15:37:57

+0

这在SQL平台中很简单,解决方案使用递归。但对于像您这样的NONsql,以及没有递归的平台,您必须使用过程和临时表。 – PerformanceDBA 2015-05-30 09:31:20

回答

3

您存储数据的方式不允许使用简单的查询来获取总子数。但是看看:

http://en.wikipedia.org/wiki/Nested_set_model

如果这样的查询将成为可能。

+3

+1,或直接在这里比较不同的模型:http://dev.mysql.com/tech-resources/articles/hierarchical-data.html – Unreason 2011-05-19 15:29:09

+0

@Unreason是的,我已经很多次穿过那篇文章,但didn现在不记得了。 ; D – Yoshi 2011-05-19 15:30:49

+0

@Unreason,Yoshi:它消失在这个位置,这里是新的:http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ – 2011-08-05 16:56:29

1

下面是一个基于PHP的解决方案:

function countChildren($startId) { 
    $directDescendents = *_query("SELECT id FROM Table WHERE parentid = ?", array($startId)); 
    $count = *_num_rows($directDescendents); 
    while($row = *_fetch_array($directDescendents)) 
     $count += countChildren($row['id']); 
    return $count; 
} 

$numChildren = countChildren(2); // Number of Children for 'B' 

更换*_num_rows*_fetch_array与正在使用的任何功能的SQL扩展。这不会像纯粹的SQL解决方案那样高效,但它会起作用。我在函数中查询的方式是假设绑定的参数,但只要你喜欢就执行查询。

+1

根据表的大小和深度对于检索整个表并在内存中执行walk/count而不是发出多个select语句可能效率更高(反过来也是如此)。 – Unreason 2011-05-19 15:47:20

2

可与非递归存储过程相当简单完成如下:

调用示例

mysql> call category_hier(1); 
+--------------+ 
| num_children | 
+--------------+ 
|   3 | 
+--------------+ 
1 row in set (0.00 sec) 

Query OK, 0 rows affected (0.00 sec) 

mysql> call category_hier(2); 
+--------------+ 
| num_children | 
+--------------+ 
|   2 | 
+--------------+ 
1 row in set (0.00 sec) 

Query OK, 0 rows affected (0.00 sec) 

完整剧本

drop table if exists categories; 
create table categories 
(
cat_id smallint unsigned not null auto_increment primary key, 
name varchar(255) not null, 
parent_cat_id smallint unsigned null, 
key (parent_cat_id) 
) 
engine = innodb; 

insert into categories (name, parent_cat_id) values 
('Location',null), 
('Color',null), 
    ('USA',1), 
     ('Illinois',3), 
     ('Chicago',3), 
    ('Black',2), 
    ('Red',2); 


drop procedure if exists category_hier; 
delimiter # 

create procedure category_hier 
(
in p_cat_id smallint unsigned 
) 
begin 

declare v_done tinyint unsigned default 0; 
declare v_depth smallint unsigned default 0; 

create temporary table hier(
parent_cat_id smallint unsigned, 
cat_id smallint unsigned, 
depth smallint unsigned default 0 
)engine = memory; 

insert into hier select parent_cat_id, cat_id, v_depth from categories where cat_id = p_cat_id; 
create temporary table tmp engine=memory select * from hier; 

/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */ 

while not v_done do 

    if exists(select 1 from categories c 
     inner join tmp on c.parent_cat_id = tmp.cat_id and tmp.depth = v_depth) then 

     insert into hier select c.parent_cat_id, c.cat_id, v_depth + 1 from categories c 
      inner join tmp on c.parent_cat_id = tmp.cat_id and tmp.depth = v_depth; 

     set v_depth = v_depth + 1;   

     truncate table tmp; 
     insert into tmp select * from hier where depth = v_depth; 

    else 
     set v_done = 1; 
    end if; 

end while; 

/* 
select 
c.cat_id, 
c.name as category_name, 
p.cat_id as parent_cat_id, 
p.name as parent_category_name, 
hier.depth 
from 
hier 
inner join categories c on hier.cat_id = c.cat_id 
left outer join categories p on hier.parent_cat_id = p.cat_id 
order by 
hier.depth; 
*/ 

select count(*) as num_children from hier where parent_cat_id is not null; 

drop temporary table if exists hier; 
drop temporary table if exists tmp; 

end # 

delimiter ; 

call category_hier(1); 

call category_hier(2); 

您可以轻松地适应这个例子来满足你的要求。

希望它能帮助:)