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

递归CTE如何在SQL Server中工作?,sql,sql-server,recursion,common-table-expression,Sql,Sql Server,Recursion,Common Table Expression,有人能帮我理解这个递归CTE是如何工作的吗 WITH RECURSIVECTE (EMPID, FULLNAME, MANAGERID, [ORGLEVEL]) AS (SELECT EMPID, FULLNAME, MANAGERID, 1 FROM RECURSIVETBL WHERE MANAGERID IS NULL UNION ALL SELECT A.EMPID,

有人能帮我理解这个递归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有两部分:

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

SELECT 
    EMPID,
    FULLNAME,
    MANAGERID,
    1 AS ORGLEVEL
FROM 
    RECURSIVETBL
WHERE 
    MANAGERID IS NULL
它似乎吸引了所有没有任何经理的员工(可能是顶级老板,或者是树形关系的根源)

递归:与
联合所有
链接,此集合必须引用声明的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
在本例中,我们正在获取(在第一次迭代中)锚定结果集(所有员工都没有经理),并通过
MANAGERID
将其与
RECURSIVETBL
连接,因此
a.EMPID
将保留先前选择的经理的员工。只要最后的每个结果集都能生成新行,这种连接就会继续下去

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

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

当你发现一名员工在
ORGLEVEL=3
时,意味着他有两名经理在管理他


一步一步地以工作为例 让我们按照这个例子:

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
  • 主播:没有经理的员工(
    ManagerID为空
    )。这将从你公司的所有头号坏蛋开始。需要注意的是,如果锚定集为空,那么整个递归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
    
  • 递归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
    
  • 对于此
    内部联接
    递归CTE
    有两行(锚定集),员工ID分别为
    1
    9
    。所以这个
    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

    由于结果是通过一个
    联合ALL
    链接的,因此此时我们得到以下结果(步骤1+2):

    这里是棘手的部分,对于下面的每个迭代,对
    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
    
  • 递归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
    
  • 考虑到在这一步中,递归CTE只保存层次级别为2的行,那么如果该联接的结果如下(级别3!):

    此集合(仅此!)将在以下递归步骤中用作
    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
    
  • 递归N°3:从工作集中的级别3s开始,联接的结果是:

    HierarchyLevel  EmployeeID  ManagerID   HierarchyRoute
    4               12          10          1 -> 3 -> 10 -> 12
    
  • 这将成为我们下一个递归步骤的工作集

  • 递归N°4:从上一步的第4级行开始,联接的结果不会产生任何行(没有员工将EmployeeID 12作为ManagerID)。不返回任何行表示迭代的结束
  • 最终的结果集很高:

    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开始


    没有过帐数据无法提供详细信息。

    您能在这里更具体一点吗?特别是,你想解释什么?你问过写rCTE的人吗?首先,我想知道“B.[ORGLEVEL]+1”部分,我想知道它是如何迭代的,以及它是如何定义组织级别的。我知道你想解释什么,但我想知道“B.[ORGLEVEL]+1”这一部分。我不知道它是如何定义组织级别以及如何迭代的。为什么它不在第二个员工之后迭代?@VijayThapa我在末尾添加了一条关于组织级别的评论。但是它如何区分级别?我需要它explained@VijayThapa我添加了一个分步指南,让我知道还有一些豆豆谢谢你的完整指导:)对不起,我来晚了。
    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