0

我是递归 CTE 的新手。我正在尝试开发一个 CTE,它将返回每个经理姓名下的所有员工。所以我有两张桌子:people_rvstaff_rv

People_rv 表包含所有人员,包括经理和员工。Staff_rv仅包含经理信息。唯一标识符人员值存储在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。

4

4 回答 4

3

据我所知,您的问题是您无法将经理与员工联系起来。

这个加入

INNER JOIN cteStaff r on r.StaffID = s2.staff_id

只需加入同一个最初的 1 级员工,回到自己身边。

更新:

还是不太对!你有一个 supervisor_id,但你仍然没有真正使用它来加入 CTE。

因此,对于此 CTE 的每次递归,您需要(不包括名称 join):

select {Level 1 Boss}, NULL (no supervisor)
union
select {new employee}, {that employee's boss}

因此,连接必须将 CTE 的 ClientID(1 级老板)连接到第二个 UNION 查询的主管字段,该字段看起来是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 级老板)连接到职员的 supervisor_id 字段。

(注意:我认为你的 staff_id 和 supervisor_id 模仿了 people_rv 表中的 people_id 值,所以这个连接应该可以正常工作。但如果它们不同(即工作人员的 supervisor_id 不是那个主管的 people_id),那么你需要编写加入,这样员工的 supervisor_id 可以加入到他们作为 CTE 中的 ClientID 存储的 people_id。)

于 2013-03-08T19:47:34.087 回答
3

您的加入可能会陷入无限循环。我会检查您希望桌子实际下降多少级。通常你加入一个类似于做的递归

 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
于 2013-03-08T21:25:05.943 回答
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
于 2013-03-08T21:31:15.863 回答
1

请注意,这可能不会解决您的问题,但它是一种希望在原始查询中看到您哪里出错的方法。

默认值为 100 级递归 - 您可以使用MAXRECURSION从 CTE 中选择的查询提示将其设置为无限制:

...
FROM cteStaff
OPTION (MAXRECURSION 0);

来自MSDN

MAXRECURSION 数

指定此查询允许的最大递归数。number 是介于 0 和 32767 之间的非负整数。指定 0 时,不应用限制。如果未指定此选项,则服务器的默认限制为 100。

在查询执行期间达到指定的或默认的 MAXRECURSION 限制数时,查询将结束并返回错误。

由于此错误,该语句的所有效果都将回滚。如果语句是 SELECT 语句,则可能返回部分结果或不返回结果。返回的任何部分结果可能不包括超出指定最大递归级别的递归级别的所有行。

于 2013-03-08T19:35:38.473 回答