我试图理解在我的SQL代码中使用CTE的概念。我已经在许多在线文章中解释了这个概念,但是我无法掌握如何迭代来呈现层次结构数据。解释R-CTE的广泛使用的示例之一是Employee and ManagerID示例,如下所示:
USE AdventureWorks
GO
WITH Emp_CTE AS (
SELECT EmployeeID, ContactID, LoginID, ManagerID, Title, BirthDate
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.EmployeeID, e.ContactID, e.LoginID, e.ManagerID, e.Title, e.BirthDate
FROM HumanResources.Employee e
INNER JOIN Emp_CTE ecte ON ecte.EmployeeID = e.ManagerID
)
SELECT *
FROM Emp_CTE
GO
锚查询将获取经理。之后,如果递归查询一次又一次地调用锚查询,并且锚查询只有一个记录即经理,那么我将无法理解其他员工。
答案 0 :(得分:3)
因此,您想了解递归CTE。
真的很简单。
首先是种子查询,它获取原始记录。
在您的情况下,就是没有经理的员工。
哪个是老板
以一个简化的示例进行演示:
EmployeeID LoginID ManagerID Title
---------- ------- --------- ------------
101 boss NULL The Boss
第二个查询将查找具有上一个记录作为经理的员工。
由于它是递归CTE,因此CTE在第二个查询中使用自身。
您可以将其视为一个循环,在该循环中它使用先前的记录来获取下一个记录。
对于该递归循环的第一次迭代,您可以得到如下内容:
EmployeeID LoginID ManagerID Title
---------- ------- --------- ------------
102 head1 101 Top Manager 1
103 head2 101 Top Manager 2
对于第二次迭代,它将使用该第一次迭代中的记录来查找下一个。
EmployeeID LoginID ManagerID Title
---------- ------- --------- ------------
104 bob 102 Department Manager 1
105 hilda 102 Department Manager 2
108 john 103 Department Manager 4
109 jane 103 Department Manager 5
对于第三次迭代,它将使用第二次迭代中的记录。
...
这一直持续到没有更多的员工加入ManagerID
然后,在所有循环之后,CTE将返回通过所有这些迭代找到的所有记录。
答案 1 :(得分:1)
嗯,递归CTE的简短介绍:
递归CTE确实是迭代的,而不是真正的递归。进行锚查询以获取一些初始结果集。有了这个设置,我们可以更深入地研究。尝试以下简单情况:
锚点的1将在UNION ALL
中导致2。该2再次传递到UNION ALL中,并将作为3返回,依此类推...
WITH recCTE AS
(
SELECT 1 AS Mycounter
UNION ALL
SELECT recCTE.MyCounter+1
FROM recCTE
WHERE recCTE.MyCounter<10
)
SELECT * FROM recCTE;
这与上面的完全相同。但是我们有两列,分别处理。
WITH recCTE AS
(
SELECT 1 AS Mycounter1, 10 AS MyCounter2
UNION ALL
SELECT recCTE.MyCounter1+1,recCTE.MyCounter2+1
FROM recCTE
WHERE recCTE.MyCounter1<10
)
SELECT * FROM recCTE;
单独运行,初始查询将返回两行。两者的counter == 1以及Nmbr列的两个不同值
WITH recCTE AS
(
SELECT MyCounter=1, Nmbr FROM(VALUES(1),(10)) A(Nmbr)
UNION ALL
SELECT recCTE.MyCounter+1, recCTE.Nmbr+1
FROM recCTE
WHERE recCTE.MyCounter<10
)
SELECT * FROM recCTE ORDER BY MyCounter,Nmbr;
现在我们返回20行,而不是前面的示例中的10行。这是因为锚的两行都是独立使用的。
在此示例中,我们将首先创建一个派生集,然后将其连接到递归CTE。猜猜为什么第一行带有“ X”而不是“ A”?
WITH SomeSet AS (SELECT * FROM (VALUES(1,'A'),(2,'B'),(3,'C'),(4,'D'),(5,'E'),(6,'F'),(7,'G'),(8,'H'),(9,'I'),(10,'J')) A(id,Letter))
,recCTE AS
(
SELECT MyCounter=1, Nmbr,'X' AS Letter FROM(VALUES(1),(10)) A(Nmbr)
UNION ALL
SELECT recCTE.MyCounter+1, recCTE.Nmbr+1, SomeSet.Letter
FROM SomeSet
INNER JOIN recCTE ON SomeSet.id=recCTE.MyCounter+1
WHERE recCTE.MyCounter<10
)
SELECT * FROM recCTE ORDER BY MyCounter,Nmbr;
WITH SomeSet AS (SELECT * FROM (VALUES(1,'A',NULL),(2,'B',1),(3,'C',2),(4,'D',3),(5,'E',4),(6,'F',5),(7,'G',6),(8,'H',7),(9,'I',8),(10,'J',9)) A(id,Letter,Previous))
,recCTE AS
(
SELECT id,Letter,Previous,' ' PreviousLetter FROM SomeSet WHERE Previous IS NULL
UNION ALL
SELECT SomeSet.id,SomeSet.Letter,SomeSet.Previous,recCTE.Letter
FROM SomeSet
INNER JOIN recCTE ON SomeSet.Previous=recCTE.id
)
SELECT * FROM recCTE:
从原则上讲,这是您的等级制度
WITH SomeSet AS (SELECT * FROM (VALUES(1,'A',NULL),(2,'B',1),(3,'C',2),(4,'D',2),(5,'E',2),(6,'F',3),(7,'G',3),(8,'H',4),(9,'I',1),(10,'J',9)) A(id,Letter,Previous))
,recCTE AS
(
SELECT id,Letter,Previous,' ' PreviousLetter FROM SomeSet WHERE Previous IS NULL
UNION ALL
SELECT SomeSet.id,SomeSet.Letter,SomeSet.Previous,recCTE.Letter
FROM SomeSet
INNER JOIN recCTE ON SomeSet.Previous=recCTE.id
)
SELECT * FROM recCTE
要点
UNION ALL
查询一样)FROM
子句中引用cte
查看如何构建LetterPath列。
WITH SomeSet AS (SELECT * FROM (VALUES(1,'A',NULL),(2,'B',1),(3,'C',2),(4,'D',2),(5,'E',2),(6,'F',3),(7,'G',3),(8,'H',4),(9,'I',1),(10,'J',9)) A(id,Letter,Previous))
,recCTE AS
(
SELECT id,Letter,Previous,' ' PreviousLetter,CAST(Letter AS VARCHAR(MAX)) AS LetterPath FROM SomeSet WHERE Previous IS NULL
UNION ALL
SELECT SomeSet.id,SomeSet.Letter,SomeSet.Previous,recCTE.Letter,recCTE.LetterPath + SomeSet.Letter
FROM SomeSet
INNER JOIN recCTE ON SomeSet.Previous=recCTE.id
)
SELECT * FROM recCTE
答案 2 :(得分:0)
这与递归步骤有关:首先,使用root进行递归的第一步,因此:
SELECT EmployeeID, ContactID, LoginID, ManagerID, Title, BirthDate
FROM HumanResources.Employee
WHERE ManagerID IS NULL
这提供了第一组记录。
第二组记录将基于第一组记录(锚)进行查询,因此它将查询所有拥有第一组经理的员工。
第二步递归将基于第二个结果集,不是锚点。
第三步将基于第三结果集,等等。