【问题标题】:Update a special id in hierarchical employee table更新分层员工表中的特殊 id
【发布时间】:2025-11-27 11:10:02
【问题描述】:

我有一个必须进行的更新。我真的卡住了。 这是一个经典的分层员工表问题,但有一个转折点。 请查看此用户树: employees

(与我的问题完全无关,只是我用谷歌图片找到的)

让我们假设以下 ID:

RM1: EmpId 1, ParentId null

AM1: EmpId 2, ParentId 1

MGR1: EmpId 3, ParentId 2

MGR2: EmpId 4, ParentId 2

EMP1: EmpId 5, ParentId 3

EMP2: EmpId 6, ParentId 3

EMP3: EmpId 7, ParentId 4

EMP4: EmpId 8, ParentId 4

我需要添加另一列,我们称之为 parentSpecialId。 此 id 是 root 以下用户的 id(AM1 和 AM2)。 AM1 和 AM2 以下的所有用户都应将 parentSpecialId 设置为 root 以下的用户。

这给了我们:

RM1: EmpId 1, ParentId null parentSpecialId null

AM1: EmpId 2, ParentId 1    parentSpecialId null

MGR1: EmpId 3, ParentId 2   parentSpecialId 2

MGR2: EmpId 4, ParentId 2   parentSpecialId 2

EMP1: EmpId 5, ParentId 3   parentSpecialId 2

EMP2: EmpId 6, ParentId 3   parentSpecialId 2

EMP3: EmpId 7, ParentId 4   parentSpecialId 2

EMP4: EmpId 8, ParentId 4   parentSpecialId 2

我只有这个 CTE,它给了我一个带有 AM1 和 AM2 的结果集。 所以我需要一直遍历到 EMPX 并使用 Id 2 更新 parentSpecialId AM1 和 AM2 的所有用户相同。当然,它需要动态的,在现实生活中我有 12 个这样的用户在 root 下。

有意义吗?

这是我的 CTE:

    WITH EmpsCTE AS
(
    SELECT id, parent, name, 0 AS EmployeeLevel
    FROM Employee
    WHERE parent = 0 
    UNION ALL
    SELECT e.id, e.parent, e.name, EmployeeLevel + 1
    FROM EmpsCTE AS p 
    JOIN Employee AS e ON e.parent = p.id 
)
SELECT id, parent, name, EmployeeLevel
From EmpsCTE where EmployeeLevel = 1 

哦,我用的是 Sql server 2008 R2

【问题讨论】:

  • 有关更奇怪的示例,演示如何生成从 Lord High Executioner (RM1) 到 Peasant (EMP*) 的完整路径,请查看 this 帖子。

标签: sql-server tsql hierarchical-data


【解决方案1】:

样本数据:

declare @T table
(
  Name varchar(10),
  EmpId int,
  ParentId int,
  ParentSpecialID int
);

insert into @T(Name, EmpId, ParentId) values
('RM1',  1, null),
('AM1',  2, 1),
('MGR1', 3, 2),
('MGR2', 4, 2),
('EMP1', 5, 3),
('EMP2', 6, 3),
('EMP3', 7, 4),
('EMP4', 8, 4);

更新声明:

with C as
(
  select T3.EmpId,
         T2.EmpId as ParentSpecialId
  from @T as T1
    inner join @T as T2
      on T1.EmpId = T2.ParentId
    inner join @T as T3
      on T2.EmpId = T3.ParentId  
  where T1.ParentId is null
  union all
  select T.EmpId,
         C.ParentSpecialId
  from @T as T
    inner join C
      on T.ParentId = C.EmpId       
)
update T
set ParentSpecialId = C.ParentSpecialId
from @T as T
  inner join C
    on T.EmpId = C.EmpId

【讨论】:

    【解决方案2】:

    处理任意深度的树:

    declare @T table ( Name varchar(16), EmpId int, ParentId int ); 
    
    insert into @T(Name, EmpId, ParentId) values 
      ('RM1',  1, null),
      ('AM1',  2, 1), 
      ('MGR1', 3, 2), 
      ('MGR2', 4, 2), 
      ('EMP1', 5, 3), 
      ('EMP2', 6, 3), 
      ('EMP3', 7, 4), 
      ('EMP4', 8, 4),
      ('AM2', 9, 1),
      ('MGR3', 10, 9),
      ('EMP5', 11, 10),
      ('Brown Noser', 12, 11),
      ('Intern', 13, 12),
      ('Coop', 14, 13),
      ('Nephew', 15, 14),
      ('Contractor', 16, 15);
    
    ; with CTE as (
      -- Start with the root(s).
      select Name, EmpId, ParentId, 0 as Depth, Cast(NULL as Int) as parentSpecialId
        from @T
        where ParentId is NULL
      union all
      -- Add the direct reports one layer at a time.
      select T.Name, T.EmpId, T.ParentId, CTE.Depth + 1, case when CTE.Depth = 1 then T.ParentId else CTE.parentSpecialId end
        from CTE inner join
          @T as T on T.ParentId = CTE.EmpID
        where T.ParentId = CTE.EmpId
      )
    select *,
      ( select Name from CTE as R where R.EmpId = CTE.ParentId ) as ReportsTo,
      ( select Name from CTE as SC where SC.EmpId = CTE.parentSpecialId ) as SubCommander
      from CTE
      order by Depth, Name
    

    感谢 Mikael Eriksson 设置示例数据!

    【讨论】:

    • 您好,很抱歉回复晚了。我采用了 user92546 cte 并更新了 Mikael 的方式。它确实有效....谢谢,你们摇滚