Sql server 如何在T-SQL中开发递归CTE?
我不熟悉递归CTE。我正在尝试开发一个CTE,它将返回每个经理名下的所有员工。所以我有两张桌子:人和职员 人员表包含所有人员,包括经理和员工。员工仅包含经理信息。Uniqueidentifier staff值存储在staff_rv中。Uniqueidentifier员工值存储在people_rv中。People_rv包含经理和员工的varchar名字和姓氏值 但当我运行以下CTE时,我得到一个错误:Sql server 如何在T-SQL中开发递归CTE?,sql-server,tsql,recursion,common-table-expression,Sql Server,Tsql,Recursion,Common Table Expression,我不熟悉递归CTE。我正在尝试开发一个CTE,它将返回每个经理名下的所有员工。所以我有两张桌子:人和职员 人员表包含所有人员,包括经理和员工。员工仅包含经理信息。Uniqueidentifier staff值存储在staff_rv中。Uniqueidentifier员工值存储在people_rv中。People_rv包含经理和员工的varchar名字和姓氏值 但当我运行以下CTE时,我得到一个错误: WITH cteStaff (ClientID, FirstName, LastName, Su
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。请注意,这可能不会解决您的问题,但有助于了解您在原始查询中的错误所在 默认为100级递归-您可以使用MAXRECURSION查询提示将其设置为无限,您可以从CTE中选择:
...
FROM cteStaff
OPTION (MAXRECURSION 0);
发件人:
最大递归数
指定此查询允许的最大递归数。数字是介于0和32767之间的非负整数。当0是
指定,则不应用任何限制。如果未指定此选项,则
服务器的默认限制为100
当在查询执行期间达到指定的或默认的MAXRECURSION限制数时,查询将结束并显示错误
返回
由于此错误,语句的所有效果都将回滚。如果语句是SELECT语句,则部分结果或否
结果可能会被返回。返回的任何部分结果可能不包括
递归级别上超过指定最大递归的所有行
水平
据我所知,你的问题是你无法将经理与员工联系起来 此连接
INNER JOIN cteStaff r on r.StaffID = s2.staff_id
只是加入了同一个最初的一级员工,回到了他自己
更新:
还是不太对!您有一个主管id,但您仍然没有实际使用该id加入CTE
因此,对于此CTE的每个递归,您需要排除名称join:
select {Level 1 Boss}, NULL (no supervisor)
union
select {new employee}, {that employee's boss}
因此,联接必须将CTE的ClientID(级别1 boss)连接到第二个联合查询的supervisor字段,该字段看起来是supervisor\u id,而不是staff\u id
完成第二个任务的连接来自我对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和level 1 boss连接到职员的supervisor\u id字段
注意:我认为您的staff\u id和supervisor\u id模仿了people\u rv表中的people\u id值,因此这种连接应该可以正常工作。但是,如果它们不同,即员工的主管id不是主管的人员id,那么您需要编写联接,以便员工的主管id可以联接到您作为客户id存储在CTE中的人员id。您的联接可能处于无限循环中。我会检查你期望桌子实际下降多少层。通常,在类似于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
下面是一个简单的递归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
这在技术上是准确的,但这不是根本问题,除非他的表中真的有101+个员工级别。你在Initech工作吗?@KyleHale我只是想告诉OP这个提示——我面前没有一个SQL Server框来测试查询-我刚刚更新了我的描述。你能看一下吗?我已经更新了我的答案,指出这是一个通用的解决方案,而不是在这个例子中实际解决他的问题。你确定递归CTE的终止段中使用的GUID是正确的吗?我想到的第一件事是递归部分永远不会终止,我想到的第一个概念是终止条件永远不会满足…而且GUID似乎至少是一个可能的目标…潜在的输入错误:在内部连接中,您的查询现在是否在似乎需要s2.supervisor\u ID的地方使用s2.staff\u ID?很好,凯尔!我修改了上面的查询,并尝试了许多不同的方法,但它并没有给我一个更深的层次。你能找出哪里错了吗?我更新了我的答案。根据我对你的模式的理解,你的联合加入看起来有点不对劲。。。