有人可以帮助我了解此递归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;
答案 0 :(得分:4)
SQL Server中的递归CTE由两部分组成:
锚点:是递归的起点。这个集合将通过递归联接进一步扩展。
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
在此示例中,我们正在获取(第一次迭代)锚定结果集(所有没有经理的员工),并将它们与RECURSIVETBL
到MANAGERID
一起加入,因此A.EMPID
将持有先前选择的经理的员工。只要每个最后的结果集都可以生成新行,这种连接就会不断进行。
对递归部分的内容有一些限制(例如,没有分组或其他嵌套的递归)。另外,因为它以UNION ALL
开头,所以它的规则也适用(列的数量和数据类型必须匹配)。
关于 ORGLEVEL ,它以设置为1的锚点开始(在此处进行硬编码)。在递归集上进一步扩展时,它将获取上一个集(锚,在第一个迭代中)并加1,因为它的表达式是B.[ORGLEVEL] + 1
,其中B
是前一个集。这意味着它以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
锚点:没有经理的员工(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个递归:使用此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 JOIN
,RecursiveCTE
有2行(锚集),雇员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。
由于结果是通过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
递归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
第3递归:从工作集中的3s级别开始,联接的结果为:
HierarchyLevel EmployeeID ManagerID HierarchyRoute
4 12 10 1 -> 3 -> 10 -> 12
这成为下一步递归步骤的工作集。
最终结果集很高:
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
以下是完整的fiddle和代码:
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
答案 1 :(得分:1)
如果您的职位超过高层管理人员,[ORGLEVEL]将始终从1开始。
没有过帐数据无法提供详细信息。