16

我有一个包含层次结构数据的表 - 例如:

childID  |  parentID
____________________
  1      |     5
  5      |     9
  9      |     20
  2      |     4
  3      |     7
  7      |     8
  8      |     8
 20      |     20
  4      |     4
  8      |     8

所需的输出:

在此处输入图像描述

我创建了一个递归 CTE,它发现我是 top 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
        )

问题:

我以 (214) 开头的显式 值执行了 CTE !childID所以它只给了我 214 的值。CTE 执行递归部分并为 childID 找到 topParent。

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

我尝试过这样做CROSS APPLY

就像是:

select * from myTable Cross Apply (
                                     ;WITH cte AS (....)
                                  )

但是恕我直言(根据我的测试!!)-这是不可能的。

将递归 CTE 放在 UDF 中的另一个想法有性能损失(我们知道 udf 的问题)。

如何创建此查询以使其真正起作用?(或一些接近的解决方案)?

这是我尝试过的

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

4

9 回答 9

21

你不能做这样的事情吗?

;WITH cte AS (....)
SELECT
    * 
FROM 
    cte
CROSS APPLY 
    dbo.myTable tbl ON cte.XXX = tbl.XXX

将CTE 定义CROSS APPLY 之后的 - 放入指向 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 输出中选择特定的子节点,您将始终获得所需的所有信息 - 包括子节点的“级别”及其顶级父节点。

于 2012-05-08T10:45:18.750 回答
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 

SE-数据

它与marc_s答案相同,不同之处在于我使用您的表变量以及您对根节点具有的事实,而 marc_s 的答案针对根节点。在我看来,最好有根节点。childID = parentIDparent_ID = nullparent_ID = null

于 2012-05-08T13:28:38.970 回答
1

我还没有时间进一步研究您的问题,并且不确定我是否理解您的问题,但是您不能使用此 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
于 2012-05-08T13:25:03.630 回答
0
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 中而不是在服务器 2000 中引入了更重要的公用表表达式,因此使用联接来获取值这是获取的基本方法子值的 parentid

于 2013-01-23T13:44:20.340 回答
0

考虑这个示例数据和相应的 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
于 2017-07-07T08:29:29.303 回答
0

在此处输入图像描述

select dbo.[fn_getIMCatPath](8)
select Cat_id,Cat_name,dbo.[fn_getIMCatPath](cat_id) from im_category_master

Create FUNCTION [dbo].[fn_getIMCatPath] (@ID INT) 
returns NVARCHAR(1000) 
AS 
BEGIN 
  DECLARE @Return   NVARCHAR(1000), 
          @parentID INT, 
          @iCount   INT 

  SET @iCount = 0 

  SELECT @Return = Cat_name, 
         @parentID = parent_id 
  FROM   im_category_master 
  WHERE  [cat_id] = @ID 

  WHILE @parentID IS NOT NULL 
    BEGIN 
        SELECT @Return = cat_name + '>' + @Return, 
               @parentID = parent_id 
        FROM   im_category_master 
        WHERE  [cat_id] = @parentID 

        SET @iCount = @iCount + 1 
        IF @parentID = -1
        BEGIN
        SET @parentID = NULL 
        END
        IF @iCount > 10 
          BEGIN 
              SET @parentID = NULL 
              SET @Return = '' 
          END 
    END 

  RETURN @Return 
END
于 2016-11-14T09:35:13.143 回答
-1
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
于 2015-03-13T11:23:34.620 回答
-1
With cteherarchy 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 cteherarchy 
于 2015-03-13T11:28:07.647 回答
-1
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
于 2013-01-22T13:09:31.300 回答