SQL Server - 来自不同表的CTE递归SUM值

时间:2016-05-09 04:19:32

标签: sql sql-server

我有一个表,其数据分层存储在名为 CsOrganization 的表中,如下表所示。

Table Name : CsOrganization
OrgId   OrgParentId    OrgName
1       NULL           X COMPANY
2       1              Administrator
3       2              Adm 1
4       2              Adm 2
5       3              Adm 1_1

然后有一个名为 EmHisOrganization 的表格,与 CsOrganization 表相关,如下表所示。

Table Name : EmHisOrganization
EmpId   OrgId    
1       2          
2       2        
3       3       
4       4        
5       5        

每位员工都会根据他们所拥有的组织获得加班数据,并将其存储在 EmOvertime 表中。

Table Name : EmOvertime
EmpId   TotalOtReal    
1       1.00          
2       2.00        
3       3.00       
4       2.00        
5       1.00 

问题是我需要根据每个组织获得TotalHours的总和。 TotalHours的总和还必须总结其孩子的所有TotalHours数据。到目前为止,我设法找出了他们的父母和孩子,但我无法弄清楚如何从中获取TotalHours数据一张不同的桌子并总结一下。据我所知,我需要加入这些表来获取TotalHours,但遗憾的是CTE Recursive不允许在语法中使用OUTER JOIN。根据上面的例子,这里是我想要的输出:

Desired Output
OrgId      OrgName       TotalHours    
1          X COMPANY     9.00
2          Administrator 9.00
3          Adm 1         4.00 
4          Adm 2         2.00
5          Adm 1_1       1.00   

请注意,Adm 1的TotalHours来自ID为3的Employee,TotalHours列中的值为3.00,ID为5的Employee,TotalHours列中的值为1.00,在所需表中为4.00。当ID为1的OrgId和TotalHours中的值为9.00时也是如此。

非常感谢任何帮助。

编辑于2016年5月9日,12:02 ,添加了表格与查询尝试的关系。

以下是关系的外观:Table Relationship

我的查询尝试(这些在每个组织上产生0.00,但是锚定查询显示正确的值,如果不推荐使用where子句):

With OrgTree (OrgId, OrgName, TotalHours) AS
(
SELECT      orgId, orgN, SUM(eReal) AS TotalHours
FROM        (SELECT OrgId AS orgId, OrgName AS orgN, CASE WHEN x.TotalOtReal IS NULL THEN 0 ELSE x.TotalOtReal END AS eReal
FROM        (SELECT f.OrgId, f.OrgName, o.TotalOtReal
FROM        dbo.CsOrganization AS f LEFT OUTER JOIN
(SELECT     OrgId, SUM(TotalOtReal) AS TotalOtReal
FROM        (SELECT a.EmpId, a.OrgId, b.TotalOtReal
FROM        dbo.EmHisOrganization AS a LEFT OUTER JOIN
(SELECT     EmpId, SUM(TotalOtReal) AS TotalOtReal
FROM        dbo.EmOvertime AS a
GROUP BY EmpId) AS b ON a.EmpId = b.EmpId) AS a_1
GROUP BY OrgId) AS o ON f.OrgId = o.OrgId
) AS x WHERE OrgId = 1) AS xx
GROUP BY orgId, orgN

UNION ALL
SELECT a.OrgId, a.OrgName, TotalHours FROM dbo.CsOrganization a
INNER JOIN OrgTree o ON a.OrgParentId = o.OrgId
)
SELECT a.OrgId, a.OrgName, SUM(a.TotalHours) AS TotalHours FROM OrgTree a
GROUP BY a.OrgId, a.OrgName

2 个答案:

答案 0 :(得分:2)

示例数据

DECLARE @CsOrganization TABLE (OrgId int, OrgParentId int, OrgName nvarchar(50));
INSERT INTO @CsOrganization (OrgId, OrgParentId, OrgName) VALUES
(1, NULL, 'X COMPANY'),
(2, 1   , 'Administrator'),
(3, 2   , 'Adm 1'),
(4, 2   , 'Adm 2'),
(5, 3   , 'Adm 1_1');

DECLARE @EmHisOrganization TABLE (EmpId int, OrgId int);
INSERT INTO @EmHisOrganization (EmpId, OrgId) VALUES
(1, 2),
(2, 2),
(3, 3),
(4, 4),
(5, 5);

DECLARE @EmOvertime TABLE (EmpId int, TotalOtReal float);
INSERT INTO @EmOvertime (EmpId, TotalOtReal) VALUES
(1, 1.00),
(2, 2.00),
(3, 3.00),
(4, 2.00),
(5, 1.00);

<强>查询

  • CTE_OrgHours计算每个组织所有员工的加班时间的简单总和。
  • CTE_Recursive是遍历组织层次结构的递归CTE。
  • 最终SELECT将遍历的树分组为树的每个节点(组织)的小时数。

逐步运行此查询,CTE-by-CTE并检查中间结果,以便更好地了解其工作原理。

WITH
CTE_OrgHours
AS
(
    SELECT
        Org.OrgId
        ,Org.OrgParentId
        ,Org.OrgName
        ,ISNULL(SUM(Overtime.TotalOtReal), 0) AS SumHours
    FROM
        @CsOrganization AS Org
        LEFT JOIN @EmHisOrganization AS Emp ON Emp.OrgId = Org.OrgID
        LEFT JOIN @EmOvertime AS Overtime ON Overtime.EmpId = Emp.EmpId
    GROUP BY
        Org.OrgId
        ,Org.OrgParentId
        ,Org.OrgName
)
,CTE_Recursive
AS
(
    SELECT
         CTE_OrgHours.OrgId
        ,CTE_OrgHours.OrgParentId
        ,CTE_OrgHours.OrgName
        ,CTE_OrgHours.SumHours
        ,1 AS Lvl
        ,CTE_OrgHours.OrgId AS StartOrgId
        ,CTE_OrgHours.OrgName AS StartOrgName
    FROM CTE_OrgHours

    UNION ALL

    SELECT
         CTE_OrgHours.OrgId
        ,CTE_OrgHours.OrgParentId
        ,CTE_OrgHours.OrgName
        ,CTE_OrgHours.SumHours
        ,CTE_Recursive.Lvl + 1 AS Lvl
        ,CTE_Recursive.StartOrgId
        ,CTE_Recursive.StartOrgName
    FROM
        CTE_OrgHours
        INNER JOIN CTE_Recursive ON CTE_Recursive.OrgId = CTE_OrgHours.OrgParentId
)
SELECT
    StartOrgId
    ,StartOrgName
    ,SUM(SumHours) AS TotalHours
FROM CTE_Recursive
GROUP BY
    StartOrgId
    ,StartOrgName
ORDER BY StartOrgId;

<强>结果

+------------+---------------+------------+
| StartOrgId | StartOrgName  | TotalHours |
+------------+---------------+------------+
|          1 | X COMPANY     |          9 |
|          2 | Administrator |          9 |
|          3 | Adm 1         |          4 |
|          4 | Adm 2         |          2 |
|          5 | Adm 1_1       |          1 |
+------------+---------------+------------+

答案 1 :(得分:0)

您如何看待Nestet Sets patern(wiki)?

OrgId   LeftIndex RightIndex    OrgName
1       1         10            X COMPANY
2       2         9             Administrator
3       3         6             Adm 1
4       7         8             Adm 2
5       4         5             Adm 1_1

SELECT O.OrgId, (SELECT Sum(EO.TotalHours)
   FROM CsOrganization CO
   INNER JOIN EmHisOrganization AS EHO ON CO.OrgId=EHO.OrgID
   INNER JOIN EmOvertime AS EO ON EHO.EmpId=EO.EmpId
   WHERE CO.LeftInde>=O.LeftIndex AND CO.RightIndex<=O.RightIndex) AS TotalHours
FROM CsOrganization O