SQL Server CTE - 查找每个childID的顶级parentID?

Roy*_*mir 16 t-sql sql-server common-table-expression sql-server-2008

我有一个包含层次结构数据的表 - 类似于:

childID  |  parentID
____________________
  1      |     5
  5      |     9
  9      |     20
  2      |     4
  3      |     7
  7      |     8
  8      |     8
 20      |     20
  4      |     4
  8      |     8
Run Code Online (Sandbox Code Playgroud)

期望的输出:

在此输入图像描述

我创建了一个递归CTE,它找到了我的顶部fatherID.

就像是:

;WITH cte AS (
                 SELECT a.childID
                       ,a.parentID
                       ,1 AS lvl
                 FROM   [Agent_Agents] a
                 WHERE   a.childID = 214 //<==== value to begin with !! - thats part the problem
                 UNION ALL
                 SELECT tmp.childID
                       ,tmp.parentID
                       ,cte.lvl+1
                 FROM   [Agent_Agents] tmp
                         INNER JOIN cte  ON  tmp.childID = cte.parentID
                 WHERE   cte.childID<>cte.parentID
             )
SELECT *
FROM   cte
WHERE   lvl = (
            SELECT MAX(lvl)
            FROM   cte
        )
Run Code Online (Sandbox Code Playgroud)

问题:

我以明确的 childID值执行了CTE 以(214)开始!所以它只给我214的价值.CTE执行递归部分并找到childID的topParent.

我想ForEach row in the Table- 用childID值来执行CTE !

我试过这样做CROSS APPLY:

就像是:

select * from myTable Cross Apply (
                                     ;WITH cte AS (....)
                                  )
Run Code Online (Sandbox Code Playgroud)

但恕我直言(从我的测试!!) - 这是不可能的.

将递归CTE放入UDF的另一个想法是性能损失(我们知道udf的问题).

如何创建此查询以使其实际工作?(或一些接近解决方案)?

这是我尝试过的

https://data.stackexchange.com/stackoverflow/query/edit/69458

mar*_*c_s 21

你不能这样做吗?

;WITH cte AS (....)
SELECT
    * 
FROM 
    cte
CROSS APPLY 
    dbo.myTable tbl ON cte.XXX = tbl.XXX
Run Code Online (Sandbox Code Playgroud)

将CTE定义CROSS APPLY 之后的内容放入一个引用回CTE的SQL语句中.那会不会工作?

或者: - 绕过你的逻辑 - 做一个"自上而下"的CTE,首先选择顶级节点,然后遍历hiearchy.这样,您可以轻松确定递归CTE的第一部分中的"顶级父亲" - 如下所示:

;WITH ChildParent AS
(
    SELECT
        ID,
        ParentID = ISNULL(ParentID, -1),
        SomeName, 
        PLevel = 1,   -- defines level, 1 = TOP, 2 = immediate child nodes etc.
        TopLevelFather = ID  -- define "top-level" parent node
    FROM dbo.[Agent_Agents] 
    WHERE ParentID IS NULL

    UNION ALL

    SELECT
        a.ID,
        ParentID = ISNULL(a.ParentID, -1),
        a.SomeName, 
        PLevel = cp.PLevel + 1,
        cp.TopLevelFather   -- keep selecting the same value for all child nodes
    FROM dbo.[Agent_Agents] a
    INNER JOIN ChildParent cp ON r.ParentID = cp.ID
)
SELECT  
   ID,
   ParentID,
   SomeName,
   PLevel,
   TopLevelFather   
FROM ChildParent
Run Code Online (Sandbox Code Playgroud)

这会给你这样的节点(基于你的样本数据,稍微扩展):

ID  ParentID  SomeName      PLevel  TopLevelFather
20    -1      Top#20           1          20
 4    -1      TOP#4            1           4
 8    -1      TOP#8            1           8
 7     8      ChildID = 7      2           8
 3     7      ChildID = 3      3           8
 2     4      ChildID = 2      2           4
 9    20      ChildID = 9      2          20
 5     9      ChildID = 5      3          20
 1     5      ChildID = 1      4          20
Run Code Online (Sandbox Code Playgroud)

现在,如果从此CTE输出中选择特定的子节点,您将始终获得所需的所有信息 - 包括子级的"级别"及其顶级父节点.


Mik*_*son 16

不确定我理解你在寻找什么但它可能是这个.

;WITH c 
     AS (SELECT childid, 
                parentid, 
                parentid AS topParentID 
         FROM   @myTable 
         WHERE  childid = parentid 
         UNION ALL 
         SELECT T.childid, 
                T.parentid, 
                c.topparentid 
         FROM   @myTable AS T 
                INNER JOIN c 
                        ON T.parentid = c.childid 
         WHERE  T.childid <> T.parentid) 
SELECT childid, 
       topparentid 
FROM   c 
ORDER  BY childid 
Run Code Online (Sandbox Code Playgroud)

SE-数据

这是一样的答案marc_s用,我用你的表变量,你有这样的事实差别childID = parentID为根节点,其中由marc_s答案有parent_ID = null根的节点.在我看来,最好有parent_ID = null根节点.