SQL 查询以显示树结构中的子项总数
SQL Query to show total of children in a tree structure
我正在尝试计算树结构的父节点的总值,但出于某种原因,我无法计算父节点的总值。
假设我有三个 tables
地区
GroupID ParentID Name
1 null NorthAmerica
2 null Asia
3 null Europe
4 1 NorthEast
5 1 WestCoast
6 3 UK
7 3 Germany
8 2 Hong Kong
9 2 Japan
地区会员
GroupID EmpID
4 10000
4 10001
5 10011
6 20455
6 10003
7 34567
9 43589
9 54890
8 84320
8 84560
员工销售
EmployeeID Name Sales ($)
10000 Joe $ 150,000.00
10001 Mary $ 200,000.00
10011 John $ 175,000.00
20455 Fred $ 100,000.00
10003 Bill $ 250,000.00
34567 Abe $ 142,000.00
43589 Jack $ 260,000.00
54890 Amanda $ 300,000.00
84320 Jane $ 15,000.00
84560 Oscar $ 175,000.00
目标是查询树中的不同级别并查看这些区域的总数。
例如,一个视图将显示销售额最高的地区:
NorthAmerica 525,000.00 *(The sum of NorthEast and WestCoast)*
Asia 750,000.00 *(The sum of Hong Kong and Japan)*
Europe 492,000.00 *(The sum of UK and Germany)*
另一个视图将显示区域总数(针对单亲):
NorthAmerica 525,000.00 *(Total of the region members NorthEast and WestCoast)*
Northeast 350,000.00 *(Total of NorthEast Leaves Joe and Mary)*
WestCoast 175,000.00 *(Total of WestCoast Leaves John)*
当然,这些树在分支方面可以更深,但我认为这个例子说明了我正在解决的问题。
到目前为止,使用 CTE,我可以相当轻松地浏览树结构,并且可以获得最终分支(或分支的叶子)的总计,但我似乎无法获得总计通过。
所以从上面的例子中,我可以得到以下输出:
NorthAmerica NULL
NorthEast 350,000.00
WestCoast 175,000.00
我会提供现有的代码,但实际的 tables 和连接数在我的实际 table 中有很大不同,可能只会混淆总体目标。不过这道题和我想做的差不多,但是好像不太对得起:
CTE Sum of Child Levels
非常感谢任何帮助。
构建脚本如下:
create table Regions
(
GroupID int,
ParentID int,
Name Varchar(40)
)
create table RegionMember
(
GroupID int,
empid int
)
Create Table EmployeeSales
(
EmployeeID int,
Name Varchar(50),
Sales float,
)
Insert into Regions Values
(1, null, 'NorthAmerica'),
(2, null, 'Asia'),
(3, null, 'Europe'),
(4, 1, 'NorthEast'),
(5, 1, 'WestCoast'),
(6, 3, 'UK'),
(7, 3, 'Germany'),
(8, 2, 'Hong Kong'),
(9, 2, 'Japan');
Insert into RegionMember Values
(4, 10000),
(4, 10001),
(5, 10011),
(6, 20455),
(6, 10003),
(7, 34567),
(9, 43589),
(9, 54890),
(8, 84320),
(8, 84560);
Insert into EmployeeSales Values
(10000, 'Joe', 150000),
(10001, 'Mary', 200000),
(10011, 'John', 175000),
(20455, 'Fred', 100000),
(10003, 'Bill', 250000),
(34567, 'Abe', 142000),
(43589, 'Jack', 260000),
(54890, 'Amanda', 300000),
(84320, 'Jane', 15000),
(84560, 'Oscar', 175000);
也开始 SQL Fiddle 上面的内容:http://sqlfiddle.com/#!6/4ee0c/1
我在示例数据中添加了几行,因为原始数据太简单了。这有三个级别。
Insert into Regions Values
(10, null, 'A1'),
(40, 10, 'B1'),
(50, 10, 'B2'),
(60, 10, 'B3'),
(70, 40, 'C1'),
(80, 40, 'C2');
Insert into RegionMember Values
(40, 104),
(50, 105),
(60, 106),
(70, 107),
(80, 108);
Insert into EmployeeSales Values
(104, '104', 104),
(105, '105', 105),
(106, '106', 106),
(107, '107', 107),
(108, '108', 108);
热门地区
此查询是直接的递归 CTE,它从最高级别 (WHERE ParentID IS NULL
) 开始并总结其所有子级。这里的 "trick" 是为了在我们遍历树时包含组的原始 StartID
和 StartName
,所以我们可以在最后 GROUP BY
它们。
WITH
CTE
AS
(
SELECT
Regions.GroupID AS StartID
,Regions.Name AS StartName
,Regions.GroupID
,Regions.ParentID
,Regions.Name
,1 AS Lvl
FROM Regions
WHERE ParentID IS NULL
UNION ALL
SELECT
CTE.StartID
,CTE.StartName
,Regions.GroupID
,Regions.ParentID
,Regions.Name
,CTE.Lvl + 1 AS Lvl
FROM
Regions
INNER JOIN CTE ON CTE.GroupID = Regions.ParentID
)
SELECT
CTE.StartID
,CTE.StartName
,SUM(EmployeeSales.Sales) AS SumSales
FROM
CTE
INNER JOIN RegionMember ON RegionMember.GroupID = CTE.GroupID
INNER JOIN EmployeeSales ON EmployeeSales.EmployeeID = RegionMember.empid
GROUP BY
CTE.StartID
,CTE.StartName
ORDER BY
CTE.StartID;
运行 逐步查询以了解其工作原理。
结果
+---------+--------------+----------+
| StartID | StartName | SumSales |
+---------+--------------+----------+
| 1 | NorthAmerica | 525000 |
| 2 | Asia | 750000 |
| 3 | Europe | 492000 |
| 10 | A1 | 530 |
+---------+--------------+----------+
地区合计和小计
第二个查询就没那么容易了。第一部分 CTE_Groups
与前面的查询非常相似,但带有特定起始 GroupID
的过滤器。 CTE_Sums
计算起始组及其每个子组的销售额汇总。 CTE_Totals
再次递归遍历 CTE_Sums
的结果并根据需要重复子行以获得每个组的总数,包括子摘要。
同样,运行 逐步查询,逐个 CTE 以了解其工作原理。
并非所有列都用于最终结果,但它们有助于理解中间步骤中发生的情况。
WITH
CTE_Groups
AS
(
SELECT
Regions.GroupID AS StartID
,Regions.Name AS StartName
,Regions.GroupID
,Regions.ParentID
,Regions.Name
,1 AS Lvl
FROM Regions
WHERE Regions.GroupID = 1 -- North America
--WHERE Regions.GroupID = 10
UNION ALL
SELECT
CTE_Groups.StartID
,CTE_Groups.StartName
,Regions.GroupID
,Regions.ParentID
,Regions.Name
,CTE_Groups.Lvl + 1 AS Lvl
FROM
Regions
INNER JOIN CTE_Groups ON CTE_Groups.GroupID = Regions.ParentID
)
,CTE_Sums
AS
(
SELECT
CTE_Groups.GroupID
,CTE_Groups.ParentID
,CTE_Groups.Name
,SUM(EmployeeSales.Sales) AS SumSales
FROM
CTE_Groups
LEFT JOIN RegionMember ON RegionMember.GroupID = CTE_Groups.GroupID
LEFT JOIN EmployeeSales ON EmployeeSales.EmployeeID = RegionMember.empid
GROUP BY
CTE_Groups.GroupID
,CTE_Groups.ParentID
,CTE_Groups.Name
)
,CTE_Totals
AS
(
SELECT
CTE_Sums.GroupID AS StartID
,CTE_Sums.Name AS StartName
,CTE_Sums.GroupID
,CTE_Sums.ParentID
,CTE_Sums.Name
,CTE_Sums.SumSales
,1 AS Lvl
FROM CTE_Sums
UNION ALL
SELECT
CTE_Totals.StartID
,CTE_Totals.StartName
,CTE_Sums.GroupID
,CTE_Sums.ParentID
,CTE_Sums.Name
,CTE_Totals.SumSales
,CTE_Totals.Lvl + 1 AS Lvl
FROM
CTE_Sums
INNER JOIN CTE_Totals ON CTE_Totals.ParentID = CTE_Sums.GroupID
)
SELECT
GroupID
,Name
,SUM(SumSales) AS SumTotal
FROM CTE_Totals
GROUP BY
GroupID
,Name
ORDER BY
GroupID
,Name
;
组 ID = 1 的结果
+---------+--------------+----------+
| GroupID | Name | SumTotal |
+---------+--------------+----------+
| 1 | NorthAmerica | 525000 |
| 4 | NorthEast | 350000 |
| 5 | WestCoast | 175000 |
+---------+--------------+----------+
组 ID = 10 的结果
+---------+------+----------+
| GroupID | Name | SumTotal |
+---------+------+----------+
| 10 | A1 | 530 |
| 40 | B1 | 319 |
| 50 | B2 | 105 |
| 60 | B3 | 106 |
| 70 | C1 | 107 |
| 80 | C2 | 108 |
+---------+------+----------+
我正在尝试计算树结构的父节点的总值,但出于某种原因,我无法计算父节点的总值。
假设我有三个 tables
地区
GroupID ParentID Name
1 null NorthAmerica
2 null Asia
3 null Europe
4 1 NorthEast
5 1 WestCoast
6 3 UK
7 3 Germany
8 2 Hong Kong
9 2 Japan
地区会员
GroupID EmpID
4 10000
4 10001
5 10011
6 20455
6 10003
7 34567
9 43589
9 54890
8 84320
8 84560
员工销售
EmployeeID Name Sales ($)
10000 Joe $ 150,000.00
10001 Mary $ 200,000.00
10011 John $ 175,000.00
20455 Fred $ 100,000.00
10003 Bill $ 250,000.00
34567 Abe $ 142,000.00
43589 Jack $ 260,000.00
54890 Amanda $ 300,000.00
84320 Jane $ 15,000.00
84560 Oscar $ 175,000.00
目标是查询树中的不同级别并查看这些区域的总数。
例如,一个视图将显示销售额最高的地区:
NorthAmerica 525,000.00 *(The sum of NorthEast and WestCoast)*
Asia 750,000.00 *(The sum of Hong Kong and Japan)*
Europe 492,000.00 *(The sum of UK and Germany)*
另一个视图将显示区域总数(针对单亲):
NorthAmerica 525,000.00 *(Total of the region members NorthEast and WestCoast)*
Northeast 350,000.00 *(Total of NorthEast Leaves Joe and Mary)*
WestCoast 175,000.00 *(Total of WestCoast Leaves John)*
当然,这些树在分支方面可以更深,但我认为这个例子说明了我正在解决的问题。
到目前为止,使用 CTE,我可以相当轻松地浏览树结构,并且可以获得最终分支(或分支的叶子)的总计,但我似乎无法获得总计通过。
所以从上面的例子中,我可以得到以下输出:
NorthAmerica NULL
NorthEast 350,000.00
WestCoast 175,000.00
我会提供现有的代码,但实际的 tables 和连接数在我的实际 table 中有很大不同,可能只会混淆总体目标。不过这道题和我想做的差不多,但是好像不太对得起:
CTE Sum of Child Levels
非常感谢任何帮助。
构建脚本如下:
create table Regions
(
GroupID int,
ParentID int,
Name Varchar(40)
)
create table RegionMember
(
GroupID int,
empid int
)
Create Table EmployeeSales
(
EmployeeID int,
Name Varchar(50),
Sales float,
)
Insert into Regions Values
(1, null, 'NorthAmerica'),
(2, null, 'Asia'),
(3, null, 'Europe'),
(4, 1, 'NorthEast'),
(5, 1, 'WestCoast'),
(6, 3, 'UK'),
(7, 3, 'Germany'),
(8, 2, 'Hong Kong'),
(9, 2, 'Japan');
Insert into RegionMember Values
(4, 10000),
(4, 10001),
(5, 10011),
(6, 20455),
(6, 10003),
(7, 34567),
(9, 43589),
(9, 54890),
(8, 84320),
(8, 84560);
Insert into EmployeeSales Values
(10000, 'Joe', 150000),
(10001, 'Mary', 200000),
(10011, 'John', 175000),
(20455, 'Fred', 100000),
(10003, 'Bill', 250000),
(34567, 'Abe', 142000),
(43589, 'Jack', 260000),
(54890, 'Amanda', 300000),
(84320, 'Jane', 15000),
(84560, 'Oscar', 175000);
也开始 SQL Fiddle 上面的内容:http://sqlfiddle.com/#!6/4ee0c/1
我在示例数据中添加了几行,因为原始数据太简单了。这有三个级别。
Insert into Regions Values
(10, null, 'A1'),
(40, 10, 'B1'),
(50, 10, 'B2'),
(60, 10, 'B3'),
(70, 40, 'C1'),
(80, 40, 'C2');
Insert into RegionMember Values
(40, 104),
(50, 105),
(60, 106),
(70, 107),
(80, 108);
Insert into EmployeeSales Values
(104, '104', 104),
(105, '105', 105),
(106, '106', 106),
(107, '107', 107),
(108, '108', 108);
热门地区
此查询是直接的递归 CTE,它从最高级别 (WHERE ParentID IS NULL
) 开始并总结其所有子级。这里的 "trick" 是为了在我们遍历树时包含组的原始 StartID
和 StartName
,所以我们可以在最后 GROUP BY
它们。
WITH
CTE
AS
(
SELECT
Regions.GroupID AS StartID
,Regions.Name AS StartName
,Regions.GroupID
,Regions.ParentID
,Regions.Name
,1 AS Lvl
FROM Regions
WHERE ParentID IS NULL
UNION ALL
SELECT
CTE.StartID
,CTE.StartName
,Regions.GroupID
,Regions.ParentID
,Regions.Name
,CTE.Lvl + 1 AS Lvl
FROM
Regions
INNER JOIN CTE ON CTE.GroupID = Regions.ParentID
)
SELECT
CTE.StartID
,CTE.StartName
,SUM(EmployeeSales.Sales) AS SumSales
FROM
CTE
INNER JOIN RegionMember ON RegionMember.GroupID = CTE.GroupID
INNER JOIN EmployeeSales ON EmployeeSales.EmployeeID = RegionMember.empid
GROUP BY
CTE.StartID
,CTE.StartName
ORDER BY
CTE.StartID;
运行 逐步查询以了解其工作原理。
结果
+---------+--------------+----------+
| StartID | StartName | SumSales |
+---------+--------------+----------+
| 1 | NorthAmerica | 525000 |
| 2 | Asia | 750000 |
| 3 | Europe | 492000 |
| 10 | A1 | 530 |
+---------+--------------+----------+
地区合计和小计
第二个查询就没那么容易了。第一部分 CTE_Groups
与前面的查询非常相似,但带有特定起始 GroupID
的过滤器。 CTE_Sums
计算起始组及其每个子组的销售额汇总。 CTE_Totals
再次递归遍历 CTE_Sums
的结果并根据需要重复子行以获得每个组的总数,包括子摘要。
同样,运行 逐步查询,逐个 CTE 以了解其工作原理。 并非所有列都用于最终结果,但它们有助于理解中间步骤中发生的情况。
WITH
CTE_Groups
AS
(
SELECT
Regions.GroupID AS StartID
,Regions.Name AS StartName
,Regions.GroupID
,Regions.ParentID
,Regions.Name
,1 AS Lvl
FROM Regions
WHERE Regions.GroupID = 1 -- North America
--WHERE Regions.GroupID = 10
UNION ALL
SELECT
CTE_Groups.StartID
,CTE_Groups.StartName
,Regions.GroupID
,Regions.ParentID
,Regions.Name
,CTE_Groups.Lvl + 1 AS Lvl
FROM
Regions
INNER JOIN CTE_Groups ON CTE_Groups.GroupID = Regions.ParentID
)
,CTE_Sums
AS
(
SELECT
CTE_Groups.GroupID
,CTE_Groups.ParentID
,CTE_Groups.Name
,SUM(EmployeeSales.Sales) AS SumSales
FROM
CTE_Groups
LEFT JOIN RegionMember ON RegionMember.GroupID = CTE_Groups.GroupID
LEFT JOIN EmployeeSales ON EmployeeSales.EmployeeID = RegionMember.empid
GROUP BY
CTE_Groups.GroupID
,CTE_Groups.ParentID
,CTE_Groups.Name
)
,CTE_Totals
AS
(
SELECT
CTE_Sums.GroupID AS StartID
,CTE_Sums.Name AS StartName
,CTE_Sums.GroupID
,CTE_Sums.ParentID
,CTE_Sums.Name
,CTE_Sums.SumSales
,1 AS Lvl
FROM CTE_Sums
UNION ALL
SELECT
CTE_Totals.StartID
,CTE_Totals.StartName
,CTE_Sums.GroupID
,CTE_Sums.ParentID
,CTE_Sums.Name
,CTE_Totals.SumSales
,CTE_Totals.Lvl + 1 AS Lvl
FROM
CTE_Sums
INNER JOIN CTE_Totals ON CTE_Totals.ParentID = CTE_Sums.GroupID
)
SELECT
GroupID
,Name
,SUM(SumSales) AS SumTotal
FROM CTE_Totals
GROUP BY
GroupID
,Name
ORDER BY
GroupID
,Name
;
组 ID = 1 的结果
+---------+--------------+----------+
| GroupID | Name | SumTotal |
+---------+--------------+----------+
| 1 | NorthAmerica | 525000 |
| 4 | NorthEast | 350000 |
| 5 | WestCoast | 175000 |
+---------+--------------+----------+
组 ID = 10 的结果
+---------+------+----------+
| GroupID | Name | SumTotal |
+---------+------+----------+
| 10 | A1 | 530 |
| 40 | B1 | 319 |
| 50 | B2 | 105 |
| 60 | B3 | 106 |
| 70 | C1 | 107 |
| 80 | C2 | 108 |
+---------+------+----------+