Sql server SQL Server CTE-查找每个子ID的顶级父ID?

Sql server SQL Server CTE-查找每个子ID的顶级父ID?,sql-server,sql-server-2008,tsql,common-table-expression,Sql Server,Sql Server 2008,Tsql,Common Table Expression,我有一个包含层次结构数据的表-类似于: childID | parentID ____________________ 1 | 5 5 | 9 9 | 20 2 | 4 3 | 7 7 | 8 8 | 8 20 | 20 4 | 4 8 | 8 ;WITH cte AS (

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

childID  |  parentID
____________________
  1      |     5
  5      |     9
  9      |     20
  2      |     4
  3      |     7
  7      |     8
  8      |     8
 20      |     20
  4      |     4
  8      |     8
;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
        )
select * from myTable Cross Apply (
                                     ;WITH cte AS (....)
                                  )
期望输出:

我已经创建了一个递归CTE,它发现我是顶级父ID

比如:

childID  |  parentID
____________________
  1      |     5
  5      |     9
  9      |     20
  2      |     4
  3      |     7
  7      |     8
  8      |     8
 20      |     20
  4      |     4
  8      |     8
;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
        )
select * from myTable Cross Apply (
                                     ;WITH cte AS (....)
                                  )
但是我从我的测试中得到了什么这是不可能的

我们知道,将递归CTE放入UDF的另一个想法会对UDF的性能造成影响

如何创建此查询以使其实际工作?还是某种近似的解决方案

这是我试过的


你不能这样做吗

;WITH cte AS (....)
SELECT
    * 
FROM 
    cte
CROSS APPLY 
    dbo.myTable tbl ON cte.XXX = tbl.XXX
将交叉应用放在CTE定义之后-放回引用CTE的一条SQL语句中。那不管用吗

或者:-翻转您的逻辑-执行自上而下的CTE,首先选择顶级节点,然后在层次结构中迭代。通过这种方式,您可以在递归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
根据示例数据,这将为您提供类似的节点,稍微扩展:

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

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

我还没有时间进一步研究您的问题,不确定我是否理解您的问题,但是你不能用这个svf来获取顶级父亲的身份证吗

CREATE FUNCTION [dbo].[getTopParent] (
    @ChildID INT
)

RETURNS int
AS
BEGIN
    DECLARE @result int;
    DECLARE @ParentID int;

    SET @ParentID=(
        SELECT ParentID FROM ChildParent
        WHERE ChildID = @ChildID 
    )

    IF(@ParentID IS NULL)
        SET @result = @ChildID 
    ELSE
        SET @result = [dbo].[getTopParent](@ParentID)

    RETURN @result    
END
然后,您应该能够通过以下方式找到每个顶级父级:

SELECT ChildID
    ,  [dbo].[getTopParent](ChildID) AS TopParentID
FROM ChildParent

我不知道你在找什么,但可能是这个

;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 
这与by相同,不同之处在于我使用了表变量,并且对于根节点,您有childID=parentID,而marc_s的答案对于根节点,parent_ID=null。在我看来,对于根节点,最好使用parent_ID=null

select distinct 
       a.ChildID,a.ParentID,
       --isnull(nullif(c.parentID,b.parentID),a.parentID) as toppa,
       B.parentID
       --,c.parentID
      ,isnull(nullif(d.parentID,a.parentID),c.parentID) as toppa1,a.name
from myTable a
   inner join myTable c
       on a.parentID=c.parentID
   inner join myTable b
       on b.childID=a.parentID
   inner join myTable d
       on d.childID=b.parentID

我使用了不带CTE的表达式,然后使用联接来获取子级的步骤到步骤父级,然后在SQL Server 2005中引入了更重要的公共表表达式,而不是在Server 2000中,因此使用联接来获取值这是获取子级值的parentid的基本方法

With cte as 
(
Select ChileId,Name,ParentId from tblHerarchy
where ParentId is null 
union ALL
Select h.ChileId,h.Name,h.ParentId  from cte
inner join tblHerarchy h on h.ParentId=cte.ChileId
) 
Select * from cte

考虑此示例数据和相应的SQL,以访问子记录及其顶级父记录

SQL代码:

;WITH c AS (
   SELECT Id, Name, ParentId as CategoryId, 
          Id as MainCategoryId, Name AS MainCategory 
     FROM   pmsItemCategory 
     WHERE  ParentId is null

     UNION ALL 

     SELECT T.Id, T.Name, T.ParentId,  MainCategoryId, MainCategory 
     FROM   pmsItemCategory AS T 
            INNER JOIN c  ON T.ParentId = c.Id 
     WHERE  T.ParentId is not null
    ) 

SELECT Id, Name, CategoryId, MainCategoryId, MainCategory 
FROM   c 
order by Id

我不认为它会成功-因为CTE知道如何执行和找到一个!!ID的顶级父项。请注意214值。。。。。我如何发送Foreach ID=>Cte.DoWorkForID?@RoyiNamir:有了这个ChildParent Cte,你就不能从ChildParent中选择*ID=214并得到你需要的吗?谢谢你的回答。提姆。我们知道的问题是udf在内联调用时会降低性能。。。这就是为什么我倾向于避免这种解决方案。通常的问题是:我有一个id为且parentId为的表。foreach行中的select*from…-我需要另一个具有最高父值的列。-我尝试了Cross Aply-vut递归引擎CTE不能与Cross Apply一起使用。我为所需输出添加了一个打印屏幕。我为所需输出添加了一个打印屏幕。@RoyiNamir-我对SE数据的查询返回您想要的结果。我刚刚添加了name和parentID列。我很高兴听到你为什么决定从最上面的开始,而不是从叶子开始。。。。这里的逻辑是什么,尽管它起作用了?为什么我们不能从最上面的叶子开始?@RoyiNamir从叶子节点开始,将向结果集中添加比您需要的更多的行。如果一个父项有三个子项,则该父项将在结果集中出现三次。在递归查询中,从上到下只为每个节点添加一次找到的新节点。您说:如果一个父节点有三个子节点,则该父节点将在结果集中出现三次-但根据我的问题,您应该看到三次。否?如果1有父2,5有父2,9有父2–您应该看到1,2和5,2和9,2。。。。否?请解释您的答案,而不是仅仅给出itI。我使用不带CTE的表达式,然后使用联接来获取子级的步骤到步骤父级,然后在SQL Server 2005中引入了更重要的公共表表达式,而不是在Server 2000中,因此使用联接来获取值这是获取子级值的parentid的基本方法解决方案不能回答问题。原始数据在ParentID中不包含NULL。建议的查询不会产生所需的输出。