【问题标题】:Getting 'root records' from self-referencing table从自引用表中获取“根记录”
【发布时间】:2016-07-13 04:38:45
【问题描述】:

我有一个自引用表:有一个 ID 和一个 PARENTID 列,允许将记录排序到一个层次结构中(我们称它们为记录层次结构)。

还有一个查询(我们称其为“查询 A”),它会返回此表中的记录列表。返回的一些记录是“根记录”(PARENTID = NULL),而一些是非根记录(PARENTID != NULL)。请注意,“查询 A”可以返回属于同一记录层次结构的多条记录。

我需要以最有效的方式完成(效率很重要但不是最重要的)是获取“查询 A”返回的所有记录的根记录,以便搜索“查询 A”中的非根记录他们的根记录。

【问题讨论】:

  • 那个查询是什么?存储过程?一个看法?只是普通的sql?
  • Zoher:只是一个普通的 SQL 查询。
  • 您可以发布查询吗?
  • 如果您在问题中包含表的示例数据、查询 A 返回的示例以及您对最终查询的期望结果,这将有助于理解问题并验证任何建议的解决方案.

标签: sql sql-server hierarchical-data recursive-query


【解决方案1】:

一种可能的解决方案:

declare @TableA table
(
  ID int,
  ParentID int NULL,
  Name varchar(100)
)

insert into @TableA(ID, ParentID, Name)
values
(1, NULL, 'root 1'),
(2, NULL, 'root 2'),
(3, 2, 'node 3->2'),
(4, 1, 'node 4->1'),
(5, 4, 'node 5->4->1'),
(6, 3, 'node 6->3->2'),
(7, 4, 'node 7->4->1'),
(8, 7, 'node 8->7->4->1')


;with QueryA as 
(
  /* your query could be here */
  select t.ID, t.Name
  from @TableA t
  where t.ID in (1, 3, 8)
),
Tree as
(
  select t.ID, t.ParentID, t.Name,
    case when t.ParentID is NULL then t.ID end as RootID
  from @TableA t
  /* starting from rows we have in QueryA */
  where t.ID in (select q.ID from QueryA q)

  union all

  select tt.ID, t.ParentID, t.Name,
    case when t.ParentID is NULL then t.ID end as RootID
  from @TableA t
  /* recursion to parents */
  inner join Tree tt on tt.ParentID = t.ID
)
select q.ID, q.Name, t.Name as RootName
from QueryA q
inner join Tree t on t.ID = q.ID and t.RootID is not NULL
order by 1, 2

您也可以从构建树开始而不链接到 QueryA(对于整个表)。看起来会简单一些。在这种情况下,您将仅在最终语句中引用 QueryA。

【讨论】:

    【解决方案2】:

    如果你想检索每个项目的Root项目,那么你可以使用以下方法:

    select t1.*,(case when t1.PARENTID is null then t1.ID else t1.PARENTID end ) Id_Root , 0 IsTraced into #tmp
    from TableName t1 
    left outer join TableName t2 on t1.ID=t1.PARENTID
    order by t1.PARENTID
    
    while exists(select TOP 1 * , (select PARENTID  from #tmp where ID=t1.PARENTID) Id_GrandParent from #tmp t1 where IsTraced=0 order by PARENTID desc )
    begin
            Declare @CurrentID as uniqueIdentifier
            set @CurrentID = (select TOP 1 ID from #tmp t1 where IsTraced=0 order by PARENTID desc )
            Declare @CurrentParentID as uniqueIdentifier
            set @CurrentParentID = (select TOP 1 PARENTID from #tmp t1 where IsTraced=0 order by PARENTID desc )
            Declare @CurrentGrandParentID as uniqueidentifier
            set @CurrentGrandParentID=(select PARENTID from #tmp where ID=@CurrentParentID)
    
            if(@CurrentGrandParentID is null)
            begin
                update #tmp set IsTraced=1 where ID=@CurrentID
            end
            else
            begin
                update #tmp set PARENTID= @CurrentGrandParentID, Id_Root=@CurrentGrandParentID  where ID=@CurrentID
            end
    end
    
    select ID,Id_Root
    from #tmp 
    order by PARENTID
    

    正如您在 while 循环之后看到的,您可以从 Temp Table #tmp 中检索每个元素的 IDId_Root

    【讨论】:

    • 我看不出这个答案如何满足仅获取特定根记录的要求。这些查询将选择所有这些。
    • @Daniel 我编辑了我的答案并添加了一种方法,您可以检索表中的所有项目及其根项目
    • 谢谢,我会调查的。
    【解决方案3】:

    这是一个技巧问题吗?首先,每条记录只有一个父级。并且只有根记录具有空父值。因此,您不必做任何复杂的事情来获取根记录。

    Select * from QueryA where PARENTID is null
    

    在查询引擎对所有记录进行真正详尽的搜索h 之后,为您获取所有根记录! PARENTID 不为空的所有其他记录都不是根记录,它们根据定义是子记录。

    【讨论】:

    • 请再次阅读问题。过滤 NULL 是 SQL 101 的东西。如果是这样的话,我就不会问这个问题了。
    • 显然我不明白你的问题。我开发了一个使用自引用数据集的项目管理系统,所以如果我理解您的问题,我将能够回答您的问题。归结为:有多少条记录的 PARENTID 为空?它只是 1 还是 > 1?
    猜你喜欢
    • 2014-06-25
    • 1970-01-01
    • 1970-01-01
    • 2020-07-26
    • 2020-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-09
    相关资源
    最近更新 更多