递归 CTE 如何在 SQL Server 中工作?



谁能帮我理解这个递归CTE是如何工作的?

WITH
RECURSIVECTE (EMPID, FULLNAME, MANAGERID, [ORGLEVEL]) AS
(SELECT EMPID,
FULLNAME,
MANAGERID,
1
FROM RECURSIVETBL
WHERE MANAGERID IS NULL
UNION ALL
SELECT A.EMPID,
A.FULLNAME,
A.MANAGERID,
B.[ORGLEVEL] + 1
FROM RECURSIVETBL A
JOIN RECURSIVECTE B ON A.MANAGERID = B.EMPID)
SELECT *
FROM RECURSIVECTE;

SQL Server 中的递归 CTE 有 2 个部分:

点:是递归的起点。这是一个将通过递归连接进一步扩展的集合。

SELECT 
EMPID,
FULLNAME,
MANAGERID,
1 AS ORGLEVEL
FROM 
RECURSIVETBL
WHERE 
MANAGERID IS NULL

它似乎正在获取所有没有任何经理的员工(将是顶级老板,或树关系的根源(。

递归:与UNION ALL链接,这个集合必须引用声明的CTE(因此使其成为递归的(。可以将其视为您将如何在下一个级别扩展锚点的结果。

UNION ALL
SELECT 
A.EMPID,
A.FULLNAME,
A.MANAGERID,
B.[ORGLEVEL] + 1
FROM 
RECURSIVETBL A
JOIN RECURSIVECTE B  -- Notice that we are referencing "RECURSIVECTE" which is the CTE we are declaring
ON A.MANAGERID = B.EMPID

在此示例中,我们(在第一次迭代中(获取定位结果集(所有没有经理的员工(,并通过MANAGERIDRECURSIVETBL将它们联接,因此A.EMPID将保存先前选择的经理的员工。只要每个最后一个结果集可以生成新行,这种联接就会一直持续下去。

对于可以放在递归部分的内容有一些限制(例如,没有分组或其他嵌套递归(。此外,由于它前面有一个UNION ALL,因此其规则也适用(列的数量和数据类型必须匹配(。

关于ORGLEVEL,它从设置为 1 的锚点开始(在那里是硬编码的(。当它在递归集上进一步扩展时,它会获取前一个集(锚点,在第一次迭代中(并添加 1,因为它的表达式B.[ORGLEVEL] + 1B是前一个集。这意味着它从 1(顶级老板(开始,并且为每个后代不断添加 1,从而代表组织的所有级别。

当你在ORGLEVEL = 3找到一个员工时,意味着他有 2 个经理在他身上。


一步一步的工作示例

让我们按照这个例子:

EmployeeID  ManagerID
1           NULL
2           1
3           1
4           2
5           2
6           1
7           6
8           6
9           NULL
10          3
11          3
12          10
13          9
14          9
15          13
  1. :没有经理的员工(ManagerID IS NULL(。这将从您公司的所有顶级坏蛋开始。请务必注意,如果锚点集为空,则整个递归 CTE 将为空,因为没有起点,也没有要加入的递归集。

    SELECT
    EmployeeID = E.EmployeeID,
    ManagerID = NULL, -- Always null by WHERE filter
    HierarchyLevel = 1,
    HierarchyRoute = CONVERT(VARCHAR(MAX), E.EmployeeID)
    FROM
    Employee AS E
    WHERE
    E.ManagerID IS NULL
    

这些是:

EmployeeID  ManagerID   HierarchyLevel  HierarchyRoute
1           (null)      1               1
9           (null)      1               9
  1. 递归 N°1:使用此UNION ALL递归:

    UNION ALL
    SELECT
    EmployeeID = E.EmployeeID,
    ManagerID = E.ManagerID,
    HierarchyLevel = R.HierarchyLevel + 1,
    HierarchyRoute = R.HierarchyRoute + ' -> ' + CONVERT(VARCHAR(10), E.EmployeeID)
    FROM
    RecursiveCTE AS R
    INNER JOIN Employee AS E ON R.EmployeeID = E.ManagerID
    

对于此INNER JOINRecursiveCTE有 2 行(锚点集(,员工 ID 为19。所以这个JOIN实际上会返回这个结果。

HierarchyLevel  EmployeeID  ManagerID   HierarchyRoute
2               2           1           1 -> 2
2               3           1           1 -> 3
2               6           1           1 -> 6
2               13          9           9 -> 13
2               14          9           9 -> 14

了解HierarchyRoute如何从 1 和 9 开始并移动到每个后代?我们还将HierarchyLevel增加了 1。

因为结果是由UNION ALL,此时我们有以下结果(步骤 1 + 2(:

HierarchyLevel  EmployeeID  ManagerID   HierarchyRoute
1               1           (null)      1
1               9           (null)      9
2               2           1           1 -> 2
2               3           1           1 -> 3
2               6           1           1 -> 6
2               13          9           9 -> 13
2               14          9           9 -> 14

这是棘手的部分,对于以下每次迭代,对RecursiveCTE的递归引用将仅包含上次迭代结果集,而不是累积集。这意味着对于下一次迭代,RecursiveCTE将表示以下行:

HierarchyLevel  EmployeeID  ManagerID   HierarchyRoute
2               2           1           1 -> 2
2               3           1           1 -> 3
2               6           1           1 -> 6
2               13          9           9 -> 13
2               14          9           9 -> 14
  1. 递归 N°2:遵循相同的递归表达式...

    UNION ALL
    SELECT
    EmployeeID = E.EmployeeID,
    ManagerID = E.ManagerID,
    HierarchyLevel = R.HierarchyLevel + 1,
    HierarchyRoute = R.HierarchyRoute + ' -> ' + CONVERT(VARCHAR(10), E.EmployeeID)
    FROM
    RecursiveCTE AS R
    INNER JOIN Employee AS E ON R.EmployeeID = E.ManagerID
    

考虑到在此步骤中RecursiveCTE只保存带有HierarchyLevel = 2的行,那么如果这个 JOIN 的结果如下(级别 3!

HierarchyLevel  EmployeeID  ManagerID   HierarchyRoute
3               4           2           1 -> 2 -> 4
3               5           2           1 -> 2 -> 5
3               7           6           1 -> 6 -> 7
3               8           6           1 -> 6 -> 8
3               10          3           1 -> 3 -> 10
3               11          3           1 -> 3 -> 11
3               15          13          9 -> 13 -> 15

这个集合(而且只有这个!(将在下面的递归步骤中用作RecursiveCTE,它将被添加到累积的总和中,即现在:

HierarchyLevel  EmployeeID  ManagerID   HierarchyRoute
1               1           (null)      1
1               9           (null)      9
2               2           1           1 -> 2
2               3           1           1 -> 3
2               6           1           1 -> 6
2               13          9           9 -> 13
2               14          9           9 -> 14
3               4           2           1 -> 2 -> 4
3               5           2           1 -> 2 -> 5
3               7           6           1 -> 6 -> 7
3               8           6           1 -> 6 -> 8
3               10          3           1 -> 3 -> 10
3               11          3           1 -> 3 -> 11
3               15          13          9 -> 13 -> 15
  1. 递归 N°3:从我们工作集中的级别 3 开始,连接的结果是:

    HierarchyLevel  EmployeeID  ManagerID   HierarchyRoute
    4               12          10          1 -> 3 -> 10 -> 12
    

这将成为我们下一步递归步骤的工作集。

  1. 递归 N°4:从上一步中唯一的行级别 4 开始,联接的结果不会生成任何行(没有员工将 EmployeeID 12 作为经理 ID(。不返回任何行标志着迭代的结束。

最终的结果集很高:

HierarchyLevel  EmployeeID  ManagerID   HierarchyRoute
1               1           (null)      1
1               9           (null)      9
2               2           1           1 -> 2
2               3           1           1 -> 3
2               6           1           1 -> 6
2               13          9           9 -> 13
2               14          9           9 -> 14
3               4           2           1 -> 2 -> 4
3               5           2           1 -> 2 -> 5
3               7           6           1 -> 6 -> 7
3               8           6           1 -> 6 -> 8
3               10          3           1 -> 3 -> 10
3               11          3           1 -> 3 -> 11
3               15          13          9 -> 13 -> 15
4               12          10          1 -> 3 -> 10 -> 12

这是完整的小提琴和代码:

CREATE TABLE Employee (EmployeeID INT, ManagerID INT)
INSERT INTO Employee (EmployeeID, ManagerID)
VALUES
(1, NULL),
(2, 1),
(3, 1),
(4, 2),
(5, 2),
(6, 1),
(7, 6),
(8, 6),
(9, NULL),
(10, 3),
(11, 3),
(12, 10),
(13, 9),
(14, 9),
(15, 13)
;WITH RecursiveCTE AS
(
SELECT
EmployeeID = E.EmployeeID,
ManagerID = NULL, -- Always null by WHERE filter
HierarchyLevel = 1,
HierarchyRoute = CONVERT(VARCHAR(MAX), E.EmployeeID)
FROM
Employee AS E
WHERE
E.ManagerID IS NULL
UNION ALL
SELECT
EmployeeID = E.EmployeeID,
ManagerID = E.ManagerID,
HierarchyLevel = R.HierarchyLevel + 1,
HierarchyRoute = R.HierarchyRoute + ' -> ' + CONVERT(VARCHAR(10), E.EmployeeID)
FROM
RecursiveCTE AS R
INNER JOIN Employee AS E ON R.EmployeeID = E.ManagerID
)
SELECT
R.HierarchyLevel,
R.EmployeeID,
R.ManagerID,
R.HierarchyRoute
FROM
RecursiveCTE AS R
ORDER BY
R.HierarchyLevel,
R.EmployeeID

如果你有超过顶级经理 [ORGLEVEL] 将始终从 1 开始。

没有发布数据无法提供详细信息。

最新更新