通过 SQL 中的层次结构进行计算
Calculations through a hierarchy in SQL
我正在尝试通过在层次结构中导航来执行一些计算。在下面的简单示例中,组织有人数并且可以与上级组织相关联,人数仅为 "leafs" 组织指定。我想使用简单的规则计算层次结构中一直向上的人数: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;
这个查询 returns:
层级确定正确,但人数计算不正确(英国应为150,法国应为300,最高层应为450)。好像CTE适合自上而下导航,但不适合自下而上?
您需要遍历每个非叶节点的层次结构,并对来自该节点的所有路径求和。
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];
试试这个:
/***** 数据 *************/
-- 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
/**** 结束查询 ******/
只是另一个使用数据类型 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
Returns
我正在尝试通过在层次结构中导航来执行一些计算。在下面的简单示例中,组织有人数并且可以与上级组织相关联,人数仅为 "leafs" 组织指定。我想使用简单的规则计算层次结构中一直向上的人数: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;
这个查询 returns:
层级确定正确,但人数计算不正确(英国应为150,法国应为300,最高层应为450)。好像CTE适合自上而下导航,但不适合自下而上?
您需要遍历每个非叶节点的层次结构,并对来自该节点的所有路径求和。
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];
试试这个: /***** 数据 *************/
-- 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
/**** 结束查询 ******/
只是另一个使用数据类型 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
Returns