我是递归CTE的新手。我正在尝试开发一个CTE,它将返回每个经理名下的所有员工。所以我有两个表:people_rv
和staff_rv
People_rv表包含所有人员,包括经理和员工。 Staff_rv
仅包含经理信息。 Uniqueidentifier员工值存储在Staff_rv
中。 Uniqueidentifier员工值存储在people_rv
中。 People_rv
包含经理和员工的varchar名字和姓氏值。
但是当我运行以下CTE时,我收到一个错误:
WITH
cteStaff (ClientID, FirstName, LastName, SupervisorID, EmpLevel)
AS
(
SELECT p.people_id, p.first_name, p.last_name, s.supervisor_id,1
FROM people_rv p JOIN staff_rv s on s.people_id = p.people_id
WHERE s.supervisor_id = '95E16819-8C3A-4098-9430-08F0E3B764E1'
UNION ALL
SELECT p2.people_id, p2.first_name, p2.last_name, s2.supervisor_id, r.EmpLevel + 1
FROM people_rv p2 JOIN staff_rv s2 on s2.people_id = p2.people_id
INNER JOIN cteStaff r on s2.staff_id = r.ClientID
)
SELECT
FirstName + ' ' + LastName AS FullName,
EmpLevel,
(SELECT first_name + ' ' + last_name FROM people_rv p join staff_rv s on s.people_id = p.people_id
WHERE s.staff_id = cteStaff.SupervisorID) AS Manager
FROM cteStaff
OPTION (MAXRECURSION 0);
我的输出是:
Barbara G 1 Melanie K
Dawn P 1 Melanie K
Garrett M 1 Melanie K
Stephanie P 1 Melanie K
Amanda F 1 Melanie K
Amanda T 1 Melanie K
Stephanie G 1 Melanie K
Carlos H 1 Melanie K
因此它不会比第一级迭代更多。为什么不? Melanie是最受监管的人,但最左边一栏的每个人也都是主管。所以这个查询也应该返回2级。
答案 0 :(得分:3)
据我所知,你的问题是你没有将经理连接到他们的员工。
此联接
INNER JOIN cteStaff r on r.StaffID = s2.staff_id
刚刚加入同一个初级1级员工。
更新:
仍然不太正确!你有一个supervisor_id,但你仍然没有真正使用它来加入CTE。
因此,对于此CTE的每次递归,您需要(不包括名称连接):
select {Level 1 Boss}, NULL (no supervisor)
union
select {new employee}, {that employee's boss}
因此,连接必须将CTE的ClientID(1级boss)连接到第二个UNION查询的 supervisor 字段,该字段看起来是 supervisor_id ,而不是 staff_id 强>
完成第二项任务的JOIN是(从我可以看出你的staff_rv表模式):
SELECT p2.people_id, p2.first_name, p2.last_name, s2.supervisor_id, r.EmpLevel + 1
FROM people_rv p2 JOIN staff_rv s2 on s2.people_id = p2.people_id
INNER JOIN cteStaff r on s2.supervisor_id = r.ClientID
注意底部连接将r.ClientID(1级boss)连接到staffer的supervisor_id字段。
(注意:我认为你的staff_id和supervisor_id模仿了people_rv表中的people_id值,所以这个联接应该可以正常工作。但如果它们不同(即职员的supervisor_id不是主管的people_id)那么你需要编写联接,以便职员的supervisor_id可以加入他们的people_id,你将其作为ClientID存储在CTE中。)
答案 1 :(得分:3)
您的加入可能会处于无限循环中。我会检查你希望表格实际下降多少级别。通常你加入一个类似于do
的递归 ID = ParentID
包含在表格或表达式中的内容。请记住,如果您必须弥补您的关系,您还可以在递归CTE之前创建CTE。
这是一个自我执行的例子,它可能有所帮助。
Declare @table table ( PersonId int identity, PersonName varchar(512), Account int, ParentId int, Orders int);
insert into @Table values ('Brett', 1, NULL, 1000),('John', 1, 1, 100),('James', 1, 1, 200),('Beth', 1, 2, 300),('John2', 2, 4, 400);
select
PersonID
, PersonName
, Account
, ParentID
from @Table
; with recursion as
(
select
t1.PersonID
, t1.PersonName
, t1.Account
--, t1.ParentID
, cast(isnull(t2.PersonName, '')
+ Case when t2.PersonName is not null then '\' + t1.PersonName else t1.PersonName end
as varchar(255)) as fullheirarchy
, 1 as pos
, cast(t1.orders +
isnull(t2.orders,0) -- if the parent has no orders than zero
as int) as Orders
from @Table t1
left join @Table t2 on t1.ParentId = t2.PersonId
union all
select
t.PersonID
, t.PersonName
, t.Account
--, t.ParentID
, cast(r.fullheirarchy + '\' + t.PersonName as varchar(255))
, pos + 1 -- increases
, r.orders + t.orders
from @Table t
join recursion r on t.ParentId = r.PersonId
)
, b as
(
select *, max(pos) over(partition by PersonID) as maxrec -- I find the maximum occurrence of position by person
from recursion
)
select *
from b
where pos = maxrec -- finds the furthest down tree
-- and Account = 2 -- I could find just someone from a different department
答案 2 :(得分:2)
这是一个很好的简单的递归CTE来审查(它可能不是答案,但其他人在搜索如何进行递归CTE可能需要它):
-- Recursive CTE
;
WITH Years ( myYear )
AS (
-- Base case
SELECT DATEPART(year, GETDATE())
UNION ALL
-- Recursive
SELECT Years.myYear - 1
FROM Years
WHERE Years.myYear >= 2002
)
SELECT *
FROM Years
答案 3 :(得分:1)
请注意,这可能无法解决您的问题,但可以帮助您查看原始查询中出错的位置。
默认为100级递归 - 您可以使用{CT}选择的MAXRECURSION
查询提示将其设置为无限制:
...
FROM cteStaff
OPTION (MAXRECURSION 0);
来自MSDN:
MAXRECURSION编号
指定此查询允许的最大递归数。 number是0到32767之间的非负整数。当0为时 指定,不应用限制。如果未指定此选项,则 服务器的默认限制为100。
在查询执行期间达到MAXRECURSION限制的指定或默认数量时,查询结束并且错误是 返回。
由于此错误,将回滚该语句的所有效果。如果语句是SELECT语句,则部分结果或否 结果可能会被退回。返回的任何部分结果可能不包括 递归级别上的所有行超出指定的最大递归 水平。