SQL Server - 从分层表中获取根节点

car*_*987 0 sql sql-server recursion recursive-query common-table-expression

我需要获取分层表的根节点(具有 PARENT_ID = NULL):

ID | PARENT_ID 
60   NULL
21   60
11   NULL
23   11
24   21
25   23
14   24
13   25
Run Code Online (Sandbox Code Playgroud)

我想要这个结果:

ID | PARENT_ID | ROOT_ID |
-------------------------
1      NULL       NULL
2         1          1
3         1          1
4         2          1
5         4          1
6       NULL    NULL
7         6          6
8         7          6
Run Code Online (Sandbox Code Playgroud)

CTE是个好方法吗?我怎样才能创建它?

显然我可以有更多的一个根节点。我必须获取每个节点的根节点。

这就是我所做的:

;WITH RCTE AS
(
    SELECT  ID, PARENT_ID, ID as ROOT_ID
    FROM TABLE r1
        WHERE NOT EXISTS (SELECT * FROM TABLE r2 WHERE r2.ID = r1.PARENT_ID)

    UNION ALL

    SELECT rh.ID, rh.PARENT_ID, 
    CASE 
        WHEN rc.ROOT_ID = rh.ID then NULL
        ELSE ROOT_ID
    END
    FROM dbo.TABLE rh
    INNER JOIN RCTE rc ON rc.ID = rh.PARENT_ID
)
select distinct ID, PARENT_ID,  
CASE WHEN ROOT_ID = ID THEN NULL
else ROOT_ID
end ROOT_ID
from RCTE
Run Code Online (Sandbox Code Playgroud)

但这是结果:

ID | PARENT_ID | ROOT_ID |
    -------------------------
    11  NULL    NULL
    60  NULL    NULL
    13  25       11
    23  11       11
    25  23       11
    13  25       60
    14  24       60
    21  60       60
    23  11       60
    24  21       60
    25  23       60
Run Code Online (Sandbox Code Playgroud)

但正如你所看到的,我有一些重复的内容:

ID | PARENT_ID | ROOT_ID |
    -------------------------
    13  25       60
    23  11       60
    25  23       60
Run Code Online (Sandbox Code Playgroud)

提前致谢!

JNe*_*ill 5

像下面这样的东西应该可以满足您的需要。

 WITH recCTE AS
(
    SELECT ID, 
        Parent_ID AS original_Parent_ID, 
        Parent_ID as next_parent_id, 
        NULL as ROOT_ID,
        CASE WHEN Parent_ID IS NULL THEN 1 END AS is_root
    FROM yourtable

    UNION ALL
    SELECT
        reccte.id, 
        reccte.original_Parent_ID,
        yourtable.Parent_ID,
        CASE WHEN yourtable.Parent_ID IS NULL THEN reccte.next_parent_id ELSE reccte.ROOT_ID END,
        NULL
    FROM
        recCTE
        INNER JOIN yourtable ON reccte.next_parent_id = yourtable.ID        
)

SELECT ID, Original_Parent_ID as Parent_ID, ROOT_ID 
FROM reccte
WHERE ROOT_ID IS NOT NULL OR is_root = 1
ORDER BY ID;
Run Code Online (Sandbox Code Playgroud)

在递归种子(第一个 SELECT)中,我们获取所有记录并标记哪个记录已经是根。

然后,在递归项(第二个 SELECT)中,我们查找父记录以获取原始记录。如果它的父级为 NULL,则我们填充该ROOT_ID列。

最后,我们从递归 CTE 中选择填充了 ROOT_ID 的记录或已经是根记录的记录。

行动中:

CREATE TABLE yourtable (ID int, Parent_ID int);

INSERT INTO yourtable VALUES
  (1, NULL),
  (2, 1),
  (3, 1),
  (4, 2), 
  (5, 4),
  (6, NULL),
  (7, 6);
Run Code Online (Sandbox Code Playgroud)


 WITH recCTE AS
(
    SELECT ID, 
        Parent_ID AS original_Parent_ID, 
        Parent_ID as next_parent_id, 
        NULL as ROOT_ID,
        CASE WHEN Parent_ID IS NULL THEN 1 END AS is_root
    FROM yourtable

    UNION ALL
    SELECT
        reccte.id, 
        reccte.original_Parent_ID,
        yourtable.Parent_ID,
        CASE WHEN yourtable.Parent_ID IS NULL THEN reccte.next_parent_id ELSE reccte.ROOT_ID END,
        NULL
    FROM
        recCTE
        INNER JOIN yourtable ON reccte.next_parent_id = yourtable.ID        
)

SELECT ID, Original_Parent_ID as Parent_ID, ROOT_ID 
FROM reccte
WHERE ROOT_ID IS NOT NULL OR is_root = 1
ORDER BY ID;
Run Code Online (Sandbox Code Playgroud)


+----+-----------+---------+
| ID | Parent_ID | ROOT_ID |
+----+-----------+---------+
|  1 | NULL      | NULL    |
|  2 | 1         | 1       |
|  3 | 1         | 1       |
|  4 | 2         | 1       |
|  5 | 4         | 1       |
|  6 | NULL      | NULL    |
|  7 | 6         | 6       |
+----+-----------+---------+
Run Code Online (Sandbox Code Playgroud)