【问题标题】:Recursive sql - find the grandgrandparent of a child递归sql - 找到孩子的祖父母
【发布时间】:2015-09-10 05:14:06
【问题描述】:

我有一张如下表:

Id ParentID 1 99 2 9 3 1 4 2 5 4 6 3

我想要一个查询,为每个孩子提供最后一个祖先。

我的意思是想要的结果是

id Lastancestor 1 99 2 9 3 99 4 9 5 9 6 99

我有很多数据,所以我需要一些快速的东西。

谢谢。

【问题讨论】:

  • 以及来自 SO 出色帮助的示例:stackoverflow.com/questions/22909692/…
  • 是否有最大深度?
  • 99?您是否有处理神灵的计划,或者只是为他们的(单)父母使用一个幻数? NULL 怎么样?还是零?或者,只是为了难,他们自己的Id?您是否进行过任何研究或尝试解决您的问题?这里的想法是人们帮助您解决问题,但 SO 不是代码编写服务。
  • @Xristos Epitropakis 您需要更改您的数据(我知道您可能无法这样做),但 T-SQL 中的 HIERARCHYID 数据类型是为这类事情设计的。如果您有兴趣,请阅读此内容msdn.microsoft.com/en-us/library/bb677213.aspx

标签: sql sql-server tsql recursive-query ancestor


【解决方案1】:

您可以使用Recursive CTE 来完成此操作:

;WITH CTE AS (
  SELECT Id AS origId, ParentID, 0 AS lvl
  FROM mytable

  UNION ALL

  SELECT c.origId AS origId, 
         m.ParentID, lvl = lvl + 1
  FROM CTE AS c
  INNER JOIN mytable AS m ON c.ParentID = m.Id
)
SELECT origId AS id, ParentID AS Lastancestor
FROM (
  SELECT origId, ParentID,
         ROW_NUMBER() OVER (PARTITION BY origId 
                            ORDER BY lvl DESC) AS rn
  FROM CTE) AS t
WHERE t.rn = 1

这里,CTE 的锚成员就是整个表。递归在树层次结构中向上传播,同时将原始Id(如origId)沿递归链传播。一旦返回一个空集,即不再找到 c.ParentID = m.Id 匹配项,递归就会终止。

要获得所需的结果,即每个 idLastancestor,我们需要做的就是获取每个 id 具有 最大 lvl(即深度)的记录。这是使用ROW_NUMBER 窗口函数实现的。

Demo here

【讨论】:

  • 非常好的解决方案!从我这边 +1
  • 一个问题:您在现实生活中使用过这种方法吗?我真的很感激,但是 - 正如 OP 所说 - 有“大量数据”而且它必须“快速”。如果 CTE 被一张大表填满,这种递归会导致很多很多的全表扫描。我怀疑是否使用了索引...您对此有何经验?无论如何,我喜欢它并且必须做一些测试......
  • @Shnugo 这是在 SQL Server 中查询分层数据结构的首选方法。唯一的其他选择 AFAIK 是使用效率较低的游标。不过,我从未在现实生活中使用过递归 CTE。 OP 可以告诉我们它如何处理他的实际数据。
【解决方案2】:

如果有最大深度,您可以使用这种方法。您可以通过简单的复制和过去添加进一步的深度级别并进行调整。我添加了一个数据元素“19,6”以生成一个包含三个祖先和一个包含四个祖先。

只需将其粘贴到一个空的查询窗口中并执行。适应您的需求...

declare @Test table (Id int, ParentID int)

insert into @Test values
(1,99)
,(2,9)
,(3,1)
,(4,2)
,(5,4)
,(6,3)
,(19,6);


WITH Ancestors1 AS
(
    SELECT Test.*
          ,Ancestor.ParentID AS Anc1ID 
    FROM @Test AS Test
    LEFT JOIN @Test AS Ancestor ON Test.ParentID=Ancestor.Id
)
,Ancestors2 AS
(
    SELECT Ancestors1.*
          , Ancestor.ParentID AS Anc2ID 
    FROM Ancestors1
    LEFT JOIN @Test AS Ancestor ON Ancestors1.Anc1ID=Ancestor.Id
)
,Ancestors3 AS
(
    SELECT Ancestors2.*
          , Ancestor.ParentID AS Anc3ID 
    FROM Ancestors2
    LEFT JOIN @Test AS Ancestor ON Ancestors2.Anc2ID=Ancestor.Id
)
SELECT Id,*
      ,COALESCE(Anc3ID,Anc2ID,Anc1ID,ParentID)  AS LastAncId
FROM Ancestors3

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-30
    • 2018-09-08
    • 1970-01-01
    • 2020-10-05
    相关资源
    最近更新 更多