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

时间:2018-10-10 10:08:24

标签: sql sql-server recursion common-table-expression recursive-query

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

ID | PARENT_ID 
60   NULL
21   60
11   NULL
23   11
24   21
25   23
14   24
13   25

我想要这个结果:

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

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

但这是结果:

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

但是您可以看到,我有一些重复项:

ID | PARENT_ID | ROOT_ID |
    -------------------------
    13  25       60
    23  11       60
    25  23       60

谢谢!

1 个答案:

答案 0 :(得分:2)

类似以下内容应该可以满足您的需求。

 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;

在递归种子(第一个SELECT)中,我们将抓取所有记录并标记已成为根的记录。

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

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

In action:

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);


 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;


+----+-----------+---------+
| 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       |
+----+-----------+---------+