Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
显示树结构中子级总数的SQL查询_Sql_Sql Server_Tsql_Common Table Expression - Fatal编程技术网

显示树结构中子级总数的SQL查询

显示树结构中子级总数的SQL查询,sql,sql-server,tsql,common-table-expression,Sql,Sql Server,Tsql,Common Table Expression,我正在尝试计算树结构的父节点的总数,由于某种原因,无法计算父节点的总值 假设我有三张桌子 地区 GroupID ParentID Name 1 null NorthAmerica 2 null Asia 3 null Europe 4 1 NorthEast 5 1 WestCo

我正在尝试计算树结构的父节点的总数,由于某种原因,无法计算父节点的总值

假设我有三张桌子

地区

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)*
另一个视图将显示区域总计(针对单亲家长):

当然,这些树的枝干可以更深,但我认为这个例子说明了我正在解决的问题

到目前为止,使用CTE,我可以相当轻松地浏览树结构,并且可以获得最终分支(或分支的叶子)的总数,但我似乎无法获得汇总的总数

因此,从上面的示例中,我可以得到以下输出:

NorthAmerica    NULL
NorthEast       350,000.00
WestCoast       175,000.00
我会提供现有的代码,但是实际的表和连接的数量在我的实际表中有很大的不同,可能会混淆总体目标。然而,这个问题与我希望实现的目标类似,但似乎并不完全符合要求:

非常感谢您的帮助

构建脚本如下所示:

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修改上面的内容:

我在示例数据中添加了几行,因为原始数据太简单了。这有三个层次

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,它从最高级别(
其中ParentID为NULL
)开始,并汇总其所有子级。这里的“诀窍”是在遍历树时包含组的原始
StartID
StartName
,这样我们就可以在最后对它们进行
分组

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_总和
计算起始组及其每个子组的销售额汇总
CTE_总计
再次递归地遍历
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
;
GroupID=1的结果

+---------+--------------+----------+
| GroupID |     Name     | SumTotal |
+---------+--------------+----------+
|       1 | NorthAmerica |   525000 |
|       4 | NorthEast    |   350000 |
|       5 | WestCoast    |   175000 |
+---------+--------------+----------+
GroupID=10的结果

+---------+------+----------+
| GroupID | Name | SumTotal |
+---------+------+----------+
|      10 | A1   |      530 |
|      40 | B1   |      319 |
|      50 | B2   |      105 |
|      60 | B3   |      106 |
|      70 | C1   |      107 |
|      80 | C2   |      108 |
+---------+------+----------+

请编写表格创建和数据插入的脚本。然后我们可以更轻松地使用它。你将不得不使用WHILE作为你的目标。在每次迭代中,您都会得到sum.Hi-dfundako,它更新了问题,将构建脚本和链接添加到sqlfiddle。谢谢,你至少考虑过使用ROLLUP和CUBE操作符吗?我愿意使用任何最好的方法…不知道如何将ROLLUP或CUBE与这个想法结合起来,第二部分正是我想要的…非常感谢你在这方面的帮助…这让我抓狂。
+---------+------+----------+
| GroupID | Name | SumTotal |
+---------+------+----------+
|      10 | A1   |      530 |
|      40 | B1   |      319 |
|      50 | B2   |      105 |
|      60 | B3   |      106 |
|      70 | C1   |      107 |
|      80 | C2   |      108 |
+---------+------+----------+