复杂的SQL查询自引用表

时间:2015-11-15 15:18:08

标签: php mysql sql

我有一个自我参考表:

ID|NAME|PARENT_ID
1 |P   |NULL
2 |C1  | 1
3 |C2  | 1
4 |C3  | 2
5 |C4  | 4

我试图编写一个查询来获取ID的所有子级(无限级别) 例如: 当输入为1时,我希望所有的行都是1的后代,即

ID|NAME|PARENT_ID
1 |P   |NULL
2 |C1  | 1
3 |C2  | 1
4 |C3  | 2
5 |C4  | 4

当输入为2时:

ID|NAME|PARENT_ID
4 |C3  | 2

请检查此SQL Fiddle

到目前为止我到过这里:

select id as productid,name,@pv:=parent_id 
from products 
join (select @pv:=1)tmp 
where parent_id=@pv

但它只给我两个级别的记录,我需要无限级别的记录。

谢谢你, 窗扇

1 个答案:

答案 0 :(得分:1)

您可以使用以下查询来实现此目的:

select  id,
        name,
        parent_id 
from    (select * from products
         order by parent_id, id) base,
        (select @pv := '1') tmp
where   find_in_set(parent_id, @pv) > 0
and     @pv := concat(@pv, ',', id)

这是基于问题中提供的fiddle

@pv := '1'中指定的值应设置为您要选择所有后代的父级的ID。

如果父母有多个孩子,这也会有效。但是,要求每条记录parent_id < id,否则结果将不完整。

另请注意,对于非常大的数据集,此解决方案可能会变慢,因为find_in_set操作不是在列表中查找数字的最理想方式,当然不是在达到大小的列表中与返回的记录数量相同的数量级。

注意:如果您希望父节点本身也包含在结果集中,那么在上述SQL前面加上id感兴趣的where值前面的前缀子句:

select  id,
        name,
        parent_id 
from    products
where   id = '1'
union
...

备选方案1:CONNECT BY

其他一些数据库具有分层查找的特定语法,例如Oracle数据库上可用的CONNECT BY子句。 MySql不提供这样的语法。

备选方案2:更智能的标识符

如果您要分配包含分层信息的id值,事情会变得容易多了。例如,在您的情况下,这可能如下所示:

ID      | NAME
1       | P   
1-1     | C1  
1-2     | C2  
1-1-1   | C3  
1-1-1-1 | C4  

然后您的选择将如下所示:

select  id,
        name 
from    products
where   id like '1-%'

备选方案3:重复自我加入

如果您知道层次结构树的深度可以达到上限,则可以使用标准sql,如下所示:

select      p5.parent_id as parent5_id,
            p4.parent_id as parent4_id,
            p3.parent_id as parent3_id,
            p2.parent_id as parent2_id,
            p1.parent_id as parent_id,
            p1.id as product_id,
            p1.name
from        products p1
left join   products p2 on p2.id = p1.parent_id 
left join   products p3 on p3.id = p2.parent_id 
left join   products p4 on p4.id = p3.parent_id  
left join   products p5 on p5.id = p4.parent_id  
left join   products p6 on p6.id = p5.parent_id
where       1 in (p1.parent_id, 
                  p2.parent_id, 
                  p3.parent_id, 
                  p4.parent_id, 
                  p5.parent_id) 
order       by 1, 2, 3, 4, 5, 6;

请参阅此fiddle

where条件指定要检索哪个父级的父级。您可以根据需要使用更多级别扩展此查询。