【问题标题】:Summarize values from month to month汇总每个月的值
【发布时间】:2023-01-08 02:36:32
【问题描述】:

我有一张这样的桌子。

https://i.stack.imgur.com/XpPZi.png

我需要将每个 ID 和每个阶段的阶段持续时间逐月相加。如果一个阶段在一个月内没有发生,那么在这个月我需要上个月这个阶段的总和,所以每个月都是过去的每个阶段(如果这个阶段存在于当月则+当前月的值).

预期结果

https://i.stack.imgur.com/ukX0I.png

我的问题是,我没有得到当月每个阶段的总和,如果当月没有发生这个阶段,我也不会得到当月的阶段。 有人可以帮帮我吗?

CREATE TABLE [dbo].[Tab_Status_Test](
    [ID] [int] NULL,
    [Phase] [nvarchar](50) NULL,
    [Phase_duration] [int] NULL,
    [EOM_Date] [date] NULL
) ON [PRIMARY]
insert into Tab_status_test
(ID ,Phase,Phase_duration, EOM_Date)
  values
     ('1' ,'C' , '22','2021/02/28')
     ,('1' ,'A' , '13','2021/03/31')
    ,('1' ,'A' , '5','2021/03/31')
    ,('1' ,'B' , '2','2021/03/31')
    ,('1' ,'B' , '19','2021/04/30')
    ,('1' ,'A' , '3','2021/04/30')
    ,('1' ,'B' , '1','2021/04/30')
    ,('1' ,'A' , '3','2021/04/30')
    ,('1' ,'B' , '22','2021/05/31')
    ,('1' ,'C' , '22','2021/06/30')
    ,('1' ,'D' , '20','2021/07/31')
    ,('1' ,'A' , '2','2021/07/31')
    ,('2' ,'C' , '22','2021/02/28')
     ,('2' ,'A' , '13','2021/03/31')
    ,('2' ,'A' , '5','2021/03/31')
    ,('3' ,'B' , '2','2021/03/31')
    ,('3' ,'B' , '19','2021/04/30')
    ,('2' ,'A' , '3','2021/04/30')
    ,('3' ,'B' , '1','2021/04/30')
    ,('2' ,'A' , '3','2021/04/30')
    ,('2' ,'B' , '22','2021/05/31')
    ,('3' ,'C' , '22','2021/06/30')
    ,('3' ,'D' , '20','2021/07/31')
    ,('3' ,'A' , '2','2021/07/31')

这是我的代码

WITH Sum_Dur
AS
(
SELECT  ID
          ,EOM_Date
          ,phase
          ,Phase_duration

,LAG(Phase_duration) OVER (Partition BY phase, eom_date ORDER BY phase,eom_date) as PrevEvent
FROM [CM_PT].[dbo].Tab_Status_Test
)

    SELECT *,
    SUM(PrevEvent+Phase_duration) AS SummedCount

     FROM Sum_Dur
     GROUP BY   ID
          ,EOM_Date
          ,phase
          ,Phase_duration
          , PrevEvent

【问题讨论】:

标签: tsql sum lag


【解决方案1】:

您想要的主要是运行总和,您可以使用SUM() OVER(PARTITION BY ... ORDER BY ...) 获得。

由于您想要包含间隙,因此您需要生成一组完整的 ID、日期和阶段,您可以将多个 SELECT DISTINCT ... 子查询 CROSS JOINed 一起完成。

由于您的数据包含多个具有相同 ID、日期和阶段活动的条目,因此需要对该数据进行分组以避免结果中出现重复行。

最后一部分是在任何活动发生之前消除早期结果。这可以通过将其他所有内容包装为另一个子选择来应用 WHERE Phase_duration > 0 条件来完成。

结果是这样的:

SELECT *
FROM (
    -- Running totals
    SELECT I.ID, D.EOM_Date, P.Phase,
        SUM(T.Phase_duration) OVER(Partition by I.ID, P.Phase ORDER BY D.EOM_DATE) AS Phase_duration
    FROM (SELECT DISTINCT ID FROM Tab_status_test) I
    CROSS JOIN (SELECT DISTINCT EOM_Date FROM Tab_status_test) D
    CROSS JOIN (SELECT DISTINCT Phase FROM Tab_status_test) P
    LEFT JOIN (
        -- Monthly totals
        SELECT ID, EOM_Date, Phase, SUM(Phase_duration) AS Phase_duration
        FROM Tab_status_test T
        GROUP BY ID, EOM_Date, Phase
    ) T ON T.ID = I.ID AND T.EOM_Date = D.EOM_Date AND T.Phase = P.Phase
) A
WHERE A.Phase_duration > 0
ORDER BY A.ID, A.EOM_Date, A.Phase

部分结果:

ID EOM_Date Phase Phase_duration
1 2021-02-28 C 22
1 2021-03-31 A 18
1 2021-03-31 B 2
1 2021-03-31 C 22
1 2021-04-30 A 24
1 2021-04-30 B 22
1 2021-04-30 C 22

this db<>fiddle

以上假设每个月至少有一些活动。如果您可能有一个整月都没有活动的间隙,您将需要用日历 gererator 替换 distinct-date subselect。

您的“预期结果”与我得到的结果略有不同。特别是,根据您提供的数据,{ID = 1, EOM_Date = 2022-04-30, Phase = B} 的总阶段持续时间应为 22 而不是 21。

如果您想将数据排序为 C/A/B,您可以将 ORDER BY 中的 Phase 项替换为将值映射到替代排序顺序的 case 语句。类似于CASE Phase WHEN 'C' THEN 1 WHEN 'A' THEN 2 WHEN 'B' THEN 3 END

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多