通过nth child id获得最顶级的父母?

时间:2013-07-15 17:36:28

标签: mysql sql database-design greatest-n-per-group hierarchical-data

现在有一个问题我们通常使用这种技术来维护父子关系,即我们将所有实体存储在一个表中,其中 parent_id 列,并且所有最顶层父项都 0 < / strong>在parent_id列中,这是一个很好的规范化技术,我同意但也有一个缺点,它是缓慢而低效的。这主要是由每个父级的递归引起的,我们必须一次又一次地运行查询来创建一个树

SELECT id FROM `table` WHERE parent_id=something

我已经查看了一些可能尝试使用任何编程语言执行此操作的解决方案,通过一次又一次地运行查询,这会在服务器上进行加载,有些提供了存储过程,但也涉及递归。

所以我的问题是我们可以用树的一个数据库查询(连接或子查询)来做吗?

  • 如果我们知道深度或者我们不知道深度?
  • 如果可能的话,我们如何才能获得任何孩子的最高父母(即parent_id = 0)?
  • 如果不可能那么为什么这种技术如此着名,虽然它有缺陷或我们有另一种解决方案呢?

    我添加了sql小提琴,但它只有架构

FIDDLE

2 个答案:

答案 0 :(得分:3)

我不知道MYSQL是否可行,我在职业生涯中一直主要使用SQL Server。在SQL Server中,可以使用WITH语句仅使用1个查询来执行此操作。

这演示了如何在所有级别获取对象的所有子级(id = 3)

With pa as (
     select pa1.*
     From prarent as pa1
     Where id = 3
     union all
     select pa2.*
     from pa join prarent as pa2 on pa.id = pa2.parent_id
  )
select * from pa where pa.id != 3

DEMO

另一个让对象的所有父母(id = 7)达到最高

的例子
With pa as (
     select pa1.*
     From prarent as pa1
     Where id = 7
     union all
     select pa2.*
     from pa join prarent as pa2 on pa.parent_id = pa2.id
  )
select * from pa where pa.id != 7

DEMO

另一个只获得最顶层父级的例子

With pa as (
     select pa1.*
     From prarent as pa1
     Where id = 7
     union all
     select pa2.*
     from pa join prarent as pa2 on pa.parent_id = pa2.id
  )
select top 1 * 
from pa 
where pa.id != 7
order by id asc

在这个例子中,我假设id是递增的,我使用一种简单的方法(仅用于演示目的)来使用order by获得最顶层。您可以根据数据库设计使用其他技术。

DEMO

使用这种类似的技巧,你可以做更多的事情,比如获得最底层的孩子......

答案 1 :(得分:1)

如果您有很多级别或大量数据,这将是不切实际的。

否则,您可以尝试模拟递归函数的功能。即使你没有获得相同的表现,如果你没有太多的数据,也没有太多的水平,这就不重要了。

一般程序如下:

  1. 对于给定的父级,请选择具有该ID的记录
  2. 此外,选择记录为PARENT_ID的记录,并选择UNION与之前的
  3. 步骤#2中的子查询给出了第一级子节点,因此您可以编写一个选择其ID的查询,并将其用作第三个查询中的子选择,其中包含&#34;其中PARENT_ID IN (分选)&#34;
  4. 这类似于步骤#3,并以这种方式进行
  5. 它看起来像这样:(see the Fiddle here

    select *
    from prarent P0
    where id = 3
    
    union 
    
    select *
    from prarent P1
    where parent_id = 3
    
    union 
    
    select *
    from prarent P2
    where parent_id in
     (  select distinct id
        from prarent P1
        where p1.parent_id = 3
     )
    
    union 
    
    select *
    from prarent P3
    where parent_id in
     (  select distinct id
        from prarent P2
        where parent_id in
        (  select distinct id
           from prarent P1
           where p1.parent_id = 3
        )
     )