简体   繁体   中英

Sql Server 2014 - Deep Recursive Parent-Child Self Join

I am trying to build a deep recursive self-join query. Having the table like:

Id | ParentId
1  | NULL
2  | 1
3  | 1
4  | 2
5  | 3
6  | 8
7  | 9

For Id 1 my query should be fetching 1,2,3,4,5 since they are either the children of 1 or children of the children of 1. In the given example 6 and 7 should not be included in the query result.

I tried using CTE but I am getting tons of duplicates:

WITH CTE AS (
    SELECT Id, ParentId
    FROM dbo.Table
    WHERE ParentId IS NULL
UNION ALL
    SELECT t.Id, t.ParentId
    FROM dbo.Table t
    INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT * FROM CTE

Ideas?

You can try to use DISTINCT to filter duplicate rows.

;WITH CTE AS (
    SELECT Id, ParentId
    FROM T
    WHERE ParentId IS NULL
UNION ALL
    SELECT t.Id, t.ParentId
    FROM T
    INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT DISTINCT Id, ParentId
FROM CTE

Try the following query with CTE where you can set parentId by @parentID :

DECLARE @parentID INT = 1
;WITH cte AS 
(
    SELECT 
    t.ID
    , t.ParentId
    FROM @table t
),
cteParent AS
(
    SELECT 
    t.ID
    , t.ParentId
    FROM @table t
    WHERE t.ParentId IN (SELECT t1.ID FROM @table t1 WHERE T1.ParentId = @parentID)
)

SELECT 
DISTINCT c1.ID
, c1.ParentId
FROM cte c1
INNER JOIN cte c2 ON c2.ParentId = c1.ID
UNION ALL 
SELECT * 
FROM cteParent

And the sample data:

DECLARE @table TABLE
(
    ID INT
    , ParentId INT
)

INSERT INTO @table
(
    ID,
    ParentId
)
VALUES
  (1, NULL )
, (2, 1 )

, (3, 1 )

, (4, 2 )

, (5, 3 )

, (6, 8 )

, (7, 9 )

OUTPUT:

ID  ParentId
1   NULL
2   1
3   1
4   2
5   3

Try this sql script which result Parent Child Hierarchy

;WITH CTE(Id , ParentId)
AS
(
SELECT 1 , NULL  UNION ALL
SELECT 2 , 1     UNION ALL
SELECT 3 , 1     UNION ALL
SELECT 4 , 2     UNION ALL
SELECT 5 , 3     UNION ALL
SELECT 6 , 8     UNION ALL
SELECT 7 , 9
)
,Cte2
AS
(
    SELECT Id , 
          ParentId ,
         CAST('\'+ CAST(Id AS VARCHAR(MAX))AS VARCHAR(MAX)) AS [Hierarchy]
    FROM CTE
    WHERE ParentId IS NULL
    UNION ALL
    SELECT c1.Id , 
          c1.ParentId ,
        [Hierarchy]+'\'+ CAST(c1.Id AS VARCHAR(MAX)) AS [Hierarchy]
    FROM Cte2 c2
    INNER JOIN CTE c1
    ON  c1.ParentId = c2.Id
)
SELECT Id,
       RIGHT([Hierarchy],LEN([Hierarchy])-1) AS ParentChildHierarchy 
FROM Cte2

GO

Result

Id  ParentChildHierarchy
-------------------------
1    1
2    1\2
3    1\3
5    1\3\5
4    1\2\4

I don't see duplicates.

Your code returns the following on the data you provided:

Id  ParentId
1   
2   1
3   1
5   3
4   2

which is what you want.

Here is a db<>fiddle.

Here is the code:

WITH t as (
      SELECT *
      FROM (VALUES (1, NULL), (2, 1), (3, 1), (4, 2), (5, 3), (6, 8), (7, 9)
           ) v(id, parentId)
    ),
    CTE AS (
    SELECT Id, ParentId
    FROM t
    WHERE ParentId IS NULL
UNION ALL
    SELECT t.Id, t.ParentId
    FROM t
    INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT *
FROM CTE;

If you are getting duplicates in your actual result set, then you presumably have duplicates in your original table. I would recommend removing them before doing the recursive logic:

with t as (
      select distinct id, parentid
      from <your query>
     ),
     . . .

Then run the recursive logic.

This query will help you

CREATE TABLE #table( ID INT, ParentId INT )

INSERT INTO #table(ID,ParentId)
VALUES (1, NULL ), (2, 1 ), (3, 1 ), (4, 2 ), (5, 3 ), (6, 8 ), (7, 9 )

;WITH CTE AS (
SELECT ID FROM #table WHERE PARENTID IS NULL
UNION ALL
SELECT T.ID FROM #table T  
INNER JOIN  #table T1 ON T.PARENTID =T1.ID 
) SELECT * FROM CTE

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM