【问题标题】:Calculations through a hierarchy in SQL通过 SQL 中的层次结构进行计算
【发布时间】:2019-12-13 15:44:26
【问题描述】:

我正在尝试通过在层次结构中导航来执行一些计算。在下面的简单示例中,组织具有员工人数并且可以与上级组织相关联,员工人数仅为“分支”组织指定。我想使用简单的规则计算整个层次结构的人数:parent_headcount = sum(children_headcount)。 我喜欢为此使用SQL Common Table Expression 的想法,但这并不完全奏效。级别的确定有效(因为它遵循自然的自上而下的导航顺序),但不是人数确定。 您将如何解决这个问题,或者有更好的方法来执行这种自下而上的计算?

-- Define the hierachical table Org
drop table if exists Org
create table Org (
    ID int identity (1,1) not null, Name nvarchar(50), parent int null, employees int,
    constraint [PK_Org] primary key clustered (ID),
    constraint [FK_Parent] foreign key (parent) references Org(ID)
);

-- Fill it in with a simple example
insert into Org (name, parent, employees) values ('ACME', NULL, 0);
insert into Org (name, parent, employees) values ('ACME France', (select Org.ID from Org where Name = 'ACME'), 0);
insert into Org (name, parent, employees) values ('ACME UK', (select Org.ID from Org where Name = 'ACME'), 0);
insert into Org (name, parent, employees) values ('ACME Paris', (select Org.ID from Org where Name = 'ACME France'), 200);
insert into Org (name, parent, employees) values ('ACME Lyons', (select Org.ID from Org where Name = 'ACME France'), 100);
insert into Org (name, parent, employees) values ('ACME London', (select Org.ID from Org where Name = 'ACME UK'), 150);
select * from Org;

-- Try to determine the total number of employees at any level of the hierarchy
with Orgs as (
    select
        ID, name, parent, 0 as employees, 0 as level from Org where parent is NULL
    union all
    select 
        child.ID, child.name, child.parent, Orgs.employees + child.employees, level + 1 from Org child
        join Orgs on child.parent = Orgs.ID
)
select * from Orgs;

此查询返回:

级别的确定是正确的,但人数的计算不正确(英国应该是 150,法国应该是 300,450 在层级的顶部)。好像CTE适合自上而下的导航,不适合自下而上的导航?

【问题讨论】:

    标签: sql sql-server hierarchy


    【解决方案1】:

    使用数据类型hierarchyid的另一种选择

    注意:@Top 和嵌套是可选的

    示例

    Declare @Top int = null
    
    ;with cteP as (
          Select ID
                ,Parent 
                ,Name 
                ,HierID = convert(hierarchyid,concat('/',ID,'/'))
                ,employees
          From   Org 
          Where  IsNull(@Top,-1) = case when @Top is null then isnull(Parent ,-1) else ID end
          Union  All
          Select ID  = r.ID
                ,Parent  = r.Parent 
                ,Name   = r.Name
                ,HierID = convert(hierarchyid,concat(p.HierID.ToString(),r.ID,'/'))
                ,r.employees
          From   Org r
          Join   cteP p on r.Parent  = p.ID)
    Select Lvl   = A.HierID.GetLevel()
          ,A.ID
          ,A.Parent
          ,Name  = Replicate('|---',A.HierID.GetLevel()-1) + A.Name
          ,Employees = sum(B.Employees)
     From  cteP A
     Join  cteP B on B.HierID.ToString() like A.HierID.ToString()+'%'
     Group By A.ID,A.Parent,A.Name,A.HierID
     Order By A.HierID
    

    退货

    【讨论】:

    • @Quiche31 总是乐于提供帮助
    【解决方案2】:

    您需要遍历每个非叶子节点的层次结构,并将该节点的所有路径相加。

    with Orgs as (
        select
            id as [top], ID, name, parent, 0 as employees, 0 as level 
            from Org g
            where exists (select 1 from Org g2 where g.ID = g2.parent)
        union all
        select 
            orgs.[top], child.ID, child.name, child.parent, Orgs.employees + child.employees, level + 1 from Org child
            join Orgs on child.parent = Orgs.ID
    )
    select [top] as id, sum(employees) employees
    from Orgs
    group by [top];
    

    Db fiddle

    【讨论】:

      【解决方案3】:

      试试这个: /***** 数据 *************/

      -- Define the hierachical table Org
      drop table Org
      create table Org (
          ID int identity (1,1) not null, Name nvarchar(50), parent int null, employees int,
          constraint [PK_Org] primary key clustered (ID),
          constraint [FK_Parent] foreign key (parent) references Org(ID)
      );
      
      -- Fill it in with a simple example
      insert into Org (name, parent, employees) values ('ACME', NULL, 0);
      insert into Org (name, parent, employees) values ('ACME France', (select Org.ID from Org where Name = 'ACME'), 0);
      insert into Org (name, parent, employees) values ('ACME UK', (select Org.ID from Org where Name = 'ACME'), 0);
      insert into Org (name, parent, employees) values ('ACME Paris', (select Org.ID from Org where Name = 'ACME France'), 200);
      insert into Org (name, parent, employees) values ('ACME Lyons', (select Org.ID from Org where Name = 'ACME France'), 100);
      insert into Org (name, parent, employees) values ('ACME London', (select Org.ID from Org where Name = 'ACME UK'), 150);
      select * from Org;
      

      /******** 结束数据 ***********/

      /********查询******/

      --尝试确定层次结构中任何级别的员工总数

      with Orgs as (
          select
              ID, name, parent,  employees, ID as RootID, 0 as level  from Org 
          union all
          select 
              child.ID , child.name, child.parent, child.employees, Orgs.RootID, level + 1  from Org child
              join Orgs on child.parent = Orgs.ID
      )
      
      select Org.Id,
             Org.Parent,
             Org.Name,
             Org.employees,
             (select max(level) from Orgs a where a.Id = Org.Id) as [Level],
             S.ProductCountIncludingChildren
      from Org
        inner join (
                   select RootID,  
                          sum(employees) as ProductCountIncludingChildren
                   from Orgs
                   group by RootID
                   ) as S
          on Org.Id = S.RootID
      
      left join Org Org2 on Org2.ID = Org.Parent
      
      order by Org.Id
      

      /**** 结束查询 ******/

      【讨论】:

        猜你喜欢
        • 2021-09-11
        • 2013-02-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-02
        • 2023-03-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多