开发者

Mysql Multiple Joins to portray a table layout of my categories

I'm trying to portray a list of categories, the sub and sub and so on like this.

Root             Sub Level 1
Personal income  Adelaide IT Solutions (AITS)
Personal income  DJ
Personal income  Ebay Sales
Personal income  IAG
Rental income    Cash Grove Pasadena
Rental income    Winston Ave Cumberland Park
Personal income  Misc Income

I've written the following SQL statement which returns results, but only for when it matches the join, for example, if I 开发者_如何转开发have a join going 3 levels deep, it will only list the categories to which has a category that deep.

I'm assuming I have to use nested select's or something similar but have no idea how to go about it.

SELECT
    c.name as 'Root',
    s.name as 'Sub Level 1',
    s2.name as 'Sub Level 2',
    s3.name as 'Sub Level 3'
FROM catergories c
    INNER JOIN catergories s ON c.id = s.parent
    INNER JOIN catergories s2 ON s.id = s2.parent
    INNER JOIN catergories s3 ON s2.id = s3.parent

Any help would be greatly appreciated.


Fairly simple non recursive adjacency list stored procedure implementation which allows you to make a single call from your application layer to the DB and retrieve the required hierarchy.

mysql> call employees_hier(1);
+--------+-----------------+-------------+-----------------+-------+
| emp_id | emp_name        | boss_emp_id | boss_name       | depth |
+--------+-----------------+-------------+-----------------+-------+
|      1 | f00             |        NULL | NULL            |     0 |
|      2 | ali later       |           1 | f00             |     1 |
|      3 | megan fox       |           1 | f00             |     1 |
|      4 | jessica alba    |           3 | megan fox       |     2 |
|      5 | eva longoria    |           3 | megan fox       |     2 |
|      6 | keira knightley |           5 | eva longoria    |     3 |
|      7 | liv tyler       |           6 | keira knightley |     4 |
|      8 | sophie marceau  |           6 | keira knightley |     4 |
+--------+-----------------+-------------+-----------------+-------+
8 rows in set (0.00 sec)

Obviously replace employees with your categories...

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);


Assuming the maximum is 3 levels deep, the simplest implementation would be using a LEFT JOIN to show all categories in the Root column, and their subcategories if they exist:

SELECT
 c.name as 'Root',
 s.name as 'Sub Level 1',
 s2.name as 'Sub Level 2',
 s3.name as 'Sub Level 3'
FROM catergories c
 LEFT JOIN catergories s ON c.id = s.parent
 LEFT JOIN catergories s2 ON s.id = s2.parent
 LEFT JOIN catergories s3 ON s2.id = s3.parent

With the query above you will have many duplicates - ALL categories will show as Root, and some of them will show again as sub categories in various levels. To have only Parents as root, I guess you can add the rule:

SELECT
 c.name as 'Root',
 s.name as 'Sub Level 1',
 s2.name as 'Sub Level 2',
 s3.name as 'Sub Level 3'
FROM catergories c
 LEFT JOIN catergories s ON c.id = s.parent
 LEFT JOIN catergories s2 ON s.id = s2.parent
 LEFT JOIN catergories s3 ON s2.id = s3.parent
WHERE c.parent IS NULL  
-- or parent=-1, or whatever a category with no parent has for in the parent column
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜