如何在MySQL中找到最老的“祖先”,其中每一行都有父或空?

时间:2016-08-10 22:55:51

标签: mysql hierarchical-data

我有一组这样的数据:

ID      NAME        PARENT
----    ------      -------
1       Obj #1      NULL
2       Obj #2      1
3       Obj #3      4
4       Obj #4      2
5       Obj #5      3
6       Obj #6      NULL
7       Obj #7      6

所以,如果我想用他们最古老的祖先来获得它们,我会得到这样的结果:

ID      NAME        OLDEST
----    ------      -------
1       Obj #1      NULL
2       Obj #2      1
3       Obj #3      1
4       Obj #4      1
5       Obj #5      1
6       Obj #6      NULL
7       Obj #7      6

如何进行查询?

3 个答案:

答案 0 :(得分:0)

由于时间不够,我只会向您展示一个解决方案(这不是最好的解决方案,但是使用重复自连接的逻辑,并且只有在知道上限时才会使用您的层次结构树)。

这是一个有效的SQL Fiddle.

测试数据生成(与您的一样,没有"名称"列):

create table tbl(id int, parent int);
insert into tbl values (1,null),(2,1),(3,4),(4,2),(5,3),(6,null),(7,6);

查询以在层次结构树中找到深度为5的最早的祖先:

select
  t1.id, coalesce(t5.id, t4.id, t3.id, t2.id) as oldest
from tbl t1
left join tbl t2 on t1.parent = t2.id
left join tbl t3 on t2.parent = t3.id
left join tbl t4 on t3.parent = t4.id
left join tbl t5 on t4.parent = t5.id
order by 1;

答案 1 :(得分:0)

一些评论推荐“嵌套集”模型,该模型在Joe Celko的SQL书籍中得到了普及。但我发现数据模型很难维护,修改数据很复杂。

我更喜欢我称之为“闭包表”的设计,我在这个广受欢迎的Stack Overflow回答中描述了这个设计:What is the most efficient/elegant way to parse a flat table into a tree?

您的查询将如下所示:

SELECT c.ID, c.NAME, a.ID AS OLDEST
FROM MyTable AS t
JOIN ClosureTable AS c ON t.ID = c.descendant 
JOIN MyTable AS a ON a.ID = c.ancestor
WHERE a.PARENT IS NULL;

我还在演示文稿Models for Hierarchical Data中介绍了这个模型(以及其他解决方案)。

以下是我发送此演示文稿的视频录制内容:Models for Hierarchical Data

我还在我的书SQL Antipatterns: Avoiding the Pitfalls of Database Programming中写了一篇关于分层数据的章节。

如果您需要一个可以在当前存储时使用数据的查询,请参阅Quassnoi在此处的聪明解决方案:https://stackoverflow.com/a/8111762/20860

但不可否认,这不是一个稳定的解决方案,因为它依赖于MySQL的无证行为。我会远离它。

如果您无法更改代码或表结构,并且今天需要它,那么您将不得不接受来自Kamil G.的答案,即使它仅限于固定的深度层次结构。对不起,没有其他解决方案。

答案 2 :(得分:0)

我通常在 MySQL 中重新设计它,以便表格如下所示:

id      name
10      Obj#1
1010    Obj#2 -- parent = 10
101020  Obj#6 -- parent = 1010 -- grandparent = 10 ... etc

我的当前设计有一个懒惰的SQL :( 6级嵌套,如果你有更深的嵌套,你可以添加更多)

SELECT a.id, a.NAME,
  ifnull(p6.PARENT, ifnull(p5.PARENT, ifnull(p4.PARENT, ifnull(p3.PARENT, ifnull(p2.PARENT, ifnull(p.PARENT, p.id)))))) AS PARENT
FROM 0_a2 AS a
LEFT JOIN 0_a2 AS p  ON a.PARENT  = p.id
LEFT JOIN 0_a2 AS p2 ON p.PARENT  = p2.id
LEFT JOIN 0_a2 AS p3 ON p2.PARENT = p3.id
LEFT JOIN 0_a2 AS p4 ON p3.PARENT = p4.id
LEFT JOIN 0_a2 AS p5 ON p4.PARENT = p5.id
LEFT JOIN 0_a2 AS p6 ON p5.PARENT = p6.id
ORDER BY a.id

enter image description here