Sql server 如何在T-SQL中开发递归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

我不熟悉递归CTE。我正在尝试开发一个CTE,它将返回每个经理名下的所有员工。所以我有两张桌子:人和职员

人员表包含所有人员,包括经理和员工。员工仅包含经理信息。Uniqueidentifier staff值存储在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。

请注意,这可能不会解决您的问题,但有助于了解您在原始查询中的错误所在

默认为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?很好,凯尔!我修改了上面的查询,并尝试了许多不同的方法,但它并没有给我一个更深的层次。你能找出哪里错了吗?我更新了我的答案。根据我对你的模式的理解,你的联合加入看起来有点不对劲。。。