在MySQL表中获取树路径

时间:2011-02-28 11:00:01

标签: mysql tree

管理MySQL数据库中的分层数据的最简单方法可能是adjacency list model。它是,给每个节点一个父:

CREATE TABLE category(
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
parent INT DEFAULT NULL);

获取父节点很容易,或者即使存在最大树深度,也可以使用此方法获取整个树:

SELECT CONCAT_WS('/', `t3`.`name`, `t2`.`name`, `t1`.`name`) AS `path`
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'xxxxx';

在很多情况下这已经足够了,但是如何为超过3个节点的树推广这个解决方案呢?即你可能有一个像“电子/音频/发射器/ FM /摩托罗拉”这样的路径。

只有一个查询可以吗?

3 个答案:

答案 0 :(得分:2)

这是一个简单的非递归存储过程来完成这项工作:

drop table if exists employees;
create table employees
(
emp_id smallint unsigned not null auto_increment primary key,
name varchar(255) not null,
boss_id smallint unsigned null,
key (boss_id)
)
engine = innodb;

insert into employees (name, boss_id) values
('f00',null), 
  ('ali later',1), 
  ('megan fox',1), 
      ('jessica alba',3), 
      ('eva longoria',3), 
         ('keira knightley',5), 
            ('liv tyler',6), 
            ('sophie marceau',6);


drop procedure if exists employees_hier;

delimiter #

create procedure employees_hier
(
in p_emp_id smallint unsigned
)
begin

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

create temporary table hier(
 boss_id smallint unsigned, 
 emp_id smallint unsigned, 
 depth smallint unsigned
)engine = memory;

insert into hier select boss_id, emp_id, v_dpth from employees where emp_id = p_emp_id;

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

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

while not v_done do

    if exists( select 1 from employees e inner join hier on e.boss_id = hier.emp_id and hier.depth = v_dpth) then

        insert into hier select e.boss_id, e.emp_id, v_dpth + 1 
            from employees e inner join emps on e.boss_id = emps.emp_id and emps.depth = v_dpth;

        set v_dpth = v_dpth + 1;            

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

    else
        set v_done = 1;
    end if;

end while;

select 
 e.emp_id,
 e.name as emp_name,
 p.emp_id as boss_emp_id,
 p.name as boss_name,
 hier.depth
from 
 hier
inner join employees e on hier.emp_id = e.emp_id
left outer join employees p on hier.boss_id = p.emp_id;

drop temporary table if exists hier;
drop temporary table if exists emps;

end #

delimiter ;

-- call this sproc from your php

call employees_hier(1);

答案 1 :(得分:0)

正如那篇文章所解释的那样,您需要知道节点的深度以生成其路径,您可以通过为不同深度的节点生成单独的查询来完成。这是嵌套集模型通常首选的原因之一。

从嵌套集模型中获取节点的路径,改编自Managing Hierarchical Data in MySQL示例:

SELECT CONCAT(GROUP_CONCAT(parent.name 
                           ORDER BY parent.lft 
                           SEPARATOR '/'), 
              '/Motorola')
  FROM nested_category AS node,
       nested_category AS parent
  WHERE node.lft BETWEEN parent.lft AND parent.rgt
    AND node.name = 'Motorola'
;

答案 2 :(得分:-1)

是的,这个解决方案是可扩展的,您可以拥有任意数量的孩子/父母, 每次插入新节点时,只需更新表中所有元素的索引。我认为这在本文中有描述

http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/