2011-03-07 50 views
3

我有一个数据库表菜单,其中包含id, name and parentid使用类似递归函数的查询选择祖先

我在数据库中有以下值,我想收集所有字段,包括使用查询的父菜单。

id name  parentid 
    1 File   0 
    2 New   1 
    3 Document  2 
    4 Image  2 
    5 Edit   0 
    6 Copy   5 
    7 Paste  5 

例子:我有2个是我目前的菜单,我要选择具有父Id 2和他们的父母以及他们父母的父母的所有字段,直到我到达山顶父(与parentid=0即)。

是否可以使用单个查询来收集它?如果是的话,它是如何实现的?

回答

2

如果你可以控制你的数据结构,那么存储这些数据的更好的方法就是让你做你需要的东西,而且这要比试着继续下去更容易。

你在做什么通常称为邻接表模型。您应该查看嵌套集合模型,这是存储和检索分层数据的更有效的方式。

这里有一个很好的教程good tutorial here

和网络的乔·塞科会给你很多链接在正确的方向,因为他一直在写关于这个多年上快速搜索。

希望这有助于使用与非递归存储过程邻接表实现

+1

“更有效的方式” - 这是一个非常讨论性的短语。在通常的树形修改中,NS性能较差**。 – zerkms 2011-03-07 04:22:58

+0

zerkms是正确的,但如果它不需要经常修改,那么它可能是答案。 – 2011-03-07 04:28:01

+0

有趣的文章,谢谢 – Simon 2011-03-07 04:45:59

4

相当简单单的呼叫解决方案。建议避免像鼠疫这样的嵌套集 - 最好留在教室里!

所有你需要做的就是从你的php中调用这些存储过程之一!

​​3210

Simples - 希望它帮助:)

示例结果

call menus_hier_downward(1); 
+---------+-----------+-----------+------------------+-------+ 
| menu_id | menu_name | parent_id | parent_menu_name | depth | 
+---------+-----------+-----------+------------------+-------+ 
|  1 | File  |  NULL | NULL    |  0 | 
|  2 | New  |   1 | File    |  1 | 
|  3 | Document |   2 | New    |  2 | 
|  4 | Image  |   2 | New    |  2 | 
+---------+-----------+-----------+------------------+-------+ 
4 rows in set (0.00 sec) 

call menus_hier_upward(3); 
+---------+-----------+-----------+------------------+-------+ 
| menu_id | menu_name | parent_id | parent_menu_name | depth | 
+---------+-----------+-----------+------------------+-------+ 
|  3 | Document |   2 | New    |  1 | 
|  2 | New  |   1 | File    |  2 | 
|  1 | File  |  NULL | NULL    |  3 | 
+---------+-----------+-----------+------------------+-------+ 
3 rows in set (0.00 sec) 

我提供你两个示例存储过程。一个向下工作。全脚本如下:

表实施例

drop table if exists menus; 
create table menus 
(
menu_id smallint unsigned not null auto_increment primary key, 
name varchar(255) not null, 
parent_id smallint unsigned null, 
key (parent_id) 
) 
engine = innodb; 

insert into menus (name, parent_id) values 
('File',null), 
('New',1), 
    ('Document',2), 
    ('Image',2), 
('Edit',null), 
('Copy',5), 
('Paste',5); 

向下存储过程

drop procedure if exists menus_hier_downward; 

delimiter # 

create procedure menus_hier_downward 
(
in p_menu_id smallint unsigned 
) 
begin 

declare v_done tinyint unsigned default(0); 
declare v_dpth smallint unsigned default(0); 

create temporary table hier(
parent_id smallint unsigned, 
menu_id smallint unsigned, 
depth smallint unsigned 
)engine = memory; 

insert into hier select parent_id, menu_id, v_dpth from menus where menu_id = p_menu_id; 

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

create temporary table tmp engine=memory select * from hier; 

while not v_done do 

    if exists(select 1 from menus m inner join hier on m.parent_id = hier.menu_id and hier.depth = v_dpth) then 

     insert into hier select m.parent_id, m.menu_id, v_dpth + 1 
      from menus m inner join tmp on m.parent_id = tmp.menu_id and tmp.depth = v_dpth; 

     set v_dpth = v_dpth + 1;    

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

    else 
     set v_done = 1; 
    end if; 

end while; 

select 
m.menu_id, 
m.name as menu_name, 
p.menu_id as parent_id, 
p.name as parent_menu_name, 
hier.depth 
from 
hier 
inner join menus m on hier.menu_id = m.menu_id 
left outer join menus p on hier.parent_id = p.menu_id; 

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

end # 

delimiter ; 

向上存储过程

drop procedure if exists menus_hier_upward; 

delimiter # 

create procedure menus_hier_upward 
(
in p_menu_id smallint unsigned 
) 
begin 

declare v_done tinyint unsigned default(0); 
declare v_dpth smallint unsigned default(0); 

create temporary table hier(
parent_id smallint unsigned, 
menu_id smallint unsigned, 
depth smallint unsigned 
)engine = memory; 

insert into hier select menu_id, null, v_dpth from menus where menu_id = p_menu_id; 

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

create temporary table tmp engine=memory select * from hier; 

while not v_done do 

    if exists(select 1 from menus m inner join hier on m.menu_id = hier.parent_id and hier.depth = v_dpth) then 

     insert into hier select m.parent_id, m.menu_id, v_dpth + 1 
      from menus m inner join tmp on m.menu_id = tmp.parent_id and tmp.depth = v_dpth; 

     set v_dpth = v_dpth + 1;    

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

    else 
     set v_done = 1; 
    end if; 

end while; 

select 
m.menu_id, 
m.name as menu_name, 
p.menu_id as parent_id, 
p.name as parent_menu_name, 
hier.depth 
from 
hier 
inner join menus m on hier.menu_id = m.menu_id 
left outer join menus p on hier.parent_id = p.menu_id; 

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

end # 

delimiter ; 
+0

请定义“向上”和“向下” - 这一切都取决于你的观点。由于这是明确的,我更喜欢“rootward”和“leafwards”。 – geoidesic 2014-12-31 12:21:16