1

我有以下两张表

表人

Id   Name
   1    A
   2    B
   3    C
   4    D
   5    E

表关系层次结构

ParentId   CHildId
   2         1
   3         2
   4         3

这将形成一个树状结构

      D
      |
      C
      |
      B
      |
      A

ParentId 和 ChildId 是 Person 表的 Id 列的外键

我需要编写可以获取我顶级父级的 SQL,即每个人的根。

以下 CTE 可以为 Each 执行此操作。我将它转换为一个函数并为每一行 Person 运行它。我在 Person 表中有大约 3k 行,这需要大约 10 秒。任何人都可以提出一种可以花费更少的方法。问题是在 CTE 之后运行的函数运行了 3k 次

DECLARE @childID INT 
SET @childID  = 1 --chield to search

;WITH RCTE AS
(
SELECT *, 1 AS Lvl FROM RelationHierarchy 
WHERE ChildID = @childID

UNION ALL

SELECT rh.*, Lvl+1 AS Lvl FROM dbo.RelationHierarchy rh
INNER JOIN RCTE rc ON rh.CHildId = rc.ParentId
 )
SELECT TOP 1 id, Name
FROM RCTE r
inner JOIN dbo.Person p ON p.id = r.ParentId
ORDER BY lvl DESC
4

3 回答 3

1

我还更新了原始问题中的答案,但没关系,这里还有一份副本:

;WITH RCTE AS
(
    SELECT  ParentId, ChildId, 1 AS Lvl FROM RelationHierarchy 

    UNION ALL

    SELECT rh.ParentId, rc.ChildId, Lvl+1 AS Lvl 
    FROM dbo.RelationHierarchy rh
    INNER JOIN RCTE rc ON rh.ChildId = rc.ParentId
)
,CTE_RN AS 
(
    SELECT *, ROW_NUMBER() OVER (PARTITION BY r.ChildID ORDER BY r.Lvl DESC) RN
    FROM RCTE r

)
SELECT pc.Id AS ChildID, pc.Name AS ChildName, r.ParentId, pp.Name AS ParentName
FROM dbo.Person pc 
LEFT JOIN CTE_RN r ON pc.id = r.CHildId AND  RN =1
LEFT JOIN dbo.Person pp ON pp.id = r.ParentId

SQLFiddle 演示

请注意,细微差别在于 CTE 的递归部分。ChildID 现在每次都从锚部分重写。另外还有 ROW_NUMBER() 函数(和新的 CTE),以便在最后获得每个孩子的顶层。

编辑 - 版本 2

在发现第一个查询的性能问题后,这里有一个改进的版本。从上到下,而不是其他方式 - 消除在 CTE 中创建额外行,在大量递归时应该更快:

;WITH RCTE AS
(
    SELECT  ParentId, CHildId, 1 AS Lvl FROM RelationHierarchy r1
    WHERE NOT EXISTS (SELECT * FROM RelationHierarchy r2 WHERE r2.CHildId = r1.ParentId)

    UNION ALL

    SELECT rc.ParentId, rh.CHildId, Lvl+1 AS Lvl 
    FROM dbo.RelationHierarchy rh
    INNER JOIN RCTE rc ON rc.CHildId = rh.ParentId
)
SELECT pc.Id AS ChildID, pc.Name AS ChildName, r.ParentId, pp.Name AS ParentName
FROM dbo.Person pc 
LEFT JOIN RCTE r ON pc.id = r.CHildId
LEFT JOIN dbo.Person pp ON pp.id = r.ParentId 

SQLFiddle 演示

于 2013-07-17T15:38:30.990 回答
0

您可以尝试使用循环。因为您将使用您的方法获得许多级别的递归:

declare @child int = 0
declare @parent int = 1 --child to search
while @child <> @parent 
BEGIN
set @child = @parent
select  @parent = Parentid from @parentchild  where ChildID = @child
END
select @parent
于 2013-07-17T16:12:21.057 回答
0

如果您想使用集合,另一种在循环中执行此操作的方法是:

SELECT *
    INTO #parentchild
from RelationHierarchy 


WHILE EXISTS
(select 1 from  #parentchild A inner join #parentchild B on A.ChildID = B.ParentID Where A.ParentID <> B.ParentID )
BEGIN
update B set B.ParentID = A.ParentID
from #parentchild A inner join #parentchild B on A.ChildID = B.ParentID
END

select * from #parentchild
于 2013-07-17T16:46:26.267 回答