【问题标题】:Rolling sum previous 3 months SQL Server前 3 个月的滚动总和 SQL Server
【发布时间】:2017-01-21 13:54:26
【问题描述】:

我的表结构如下:

Name           |Type
---------------|-----
fiscal year    | varchar
period         | varchar
country        | varchar
value          | int

我在 SQL Server 2012 中遇到了一个查询,该查询应该计算表中每个不同月份的前三个月的总和。月份之间可能存在间隔,每个月的数据都不存在,并且对于给定的年份、月份和国家/地区可能有几行。

EG:

Year    |Period |Country   |Value
--------|-------|----------|------
2016    |2      |Morovia   |100
2016    |9      |Morovia   |100
2016    |10     |Elbonia   |-20
2016    |10     |Elbonia   |2000
2016    |10     |Elbonia   |200
2016    |10     |Elbonia   |-100
2016    |10     |Elbonia   |1000
2016    |10     |Morovia   |200
2016    |10     |Elbonia   |-200
2016    |10     |Elbonia   |-200
2016    |10     |Elbonia   |100
2016    |10     |Elbonia   |60
2016    |10     |Elbonia   |40
2016    |11     |Morovia   |200
2016    |11     |Elbonia   |100

我正在尝试创建一个如下所示的结果集:

Year    |Period |Country   |3M Value
--------|-------|----------|--------
2016    |2      |Morovia   |100        - data only for this month
2016    |9      |Morovia   |100        - data only for this month
2016    |10     |Morovia   |300        - current month (200) + previous(100)
2016    |10     |Elbonia   |2880       - data only for this month
2016    |11     |Morovia   |500        - current + previous + 2 month ago
2016    |11     |Elbonia   |2980       - current month(100) + previous(2880)

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    使用Outer JOIN的另一种方法

    ;WITH cte
         AS (SELECT year,
                    period,
                    country,
                    Sum(value) AS sumvalue
             FROM   Yourtable 
             GROUP  BY year,
                       period,
                       country)
    SELECT a.Year,
           a.Period,
           a.Country,
           a.sumvalue + Isnull(Sum(b.sumvalue), 0) as [3M Value]
    FROM   cte a
           LEFT JOIN cte b
                  ON a.Country = b.Country
                     AND Datefromparts(b.[Year], b.Period, 1) IN ( Dateadd(mm, -1, Datefromparts(a.[Year], a.Period, 1)), Dateadd(mm, -2, Datefromparts(a.[Year], a.Period, 1)) )
    GROUP  BY a.Year,
              a.Period,
              a.Country,
              a.sumvalue 
    

    【讨论】:

      【解决方案2】:

      如果月份不可用,则可以使用非等值连接或外部应用:

      with t as (
            select year, period, country, sum(value) as sumvalue
            from t
            group by year, period, country
           )
      select t.*, tt.sumvalue_3month
      from t outer apply
           (select sum(t2.sumvalue) as sumvalue_3month
            from t t2
            where t.country = t2.country and
                  t2.year * 12 + t2.period >= t.year * 12 + t.period - 2 and
                  t2.year * 12 + t2.period <= t.year * 12 + t.period
           ) tt;
      

      CTE 按年、期和月汇总数据。 outer apply 然后是前三个月的总和。诀窍是将年份和期间值转换为从零开始的月份。

      其实更简单的方法是在CTE中做yyyymm计算:

      with t as (
            select year, period, country, sum(value) as sumvalue,
                   (t.year * 12 + t.period) as month_count
            from t
            group by year, period, country
           )
      select t.*, tt.sumvalue_3month
      from t outer apply
           (select sum(t2.sumvalue) as sumvalue_3month
            from t t2
            where t.country = t2.country and
                  t2.month_count >= t.month_count - 2 and
                  t2.month_count <= t.month_count
           ) tt;
      

      【讨论】:

      • @SQLZim 。 . .谢谢你。这个国家对于 OP 的预期产出显然很重要。
      • 没问题。 where 子句中的那种尾随 and 的风格从何而来?
      • 我缩进 SQL SELECT 查询,以便主要 SQL 子句在 LEFT 上对齐。 AND(就此而言,JOINON)不是主要子句(想想SELECTFROMWHEREGROUP BY 等)。
      【解决方案3】:

      假设您有另一个包含国家/地区的表

      CREATE TABLE Country(Country VARCHAR(50) PRIMARY KEY);
      INSERT INTO Country VALUES ('Morovia'),('Elbonia');
      

      然后您可以创建一个包含感兴趣时间段的所有年/月组合的表格

      CREATE TABLE YearMonth
      (
      Year INT,
      Month INT,
      PRIMARY KEY (Year, Month)
      )
      INSERT INTO YearMonth
      SELECT Year, Month
      FROM (VALUES(2015), (2016), (2017)) Y(Year)
      CROSS JOIN (VALUES(1), (2), (3),(4), (5), (6),(7), (8), (9), (10), (11), (12)) M(Month)
      

      然后外连接到 (Demo) 上,如下所示。对于每个国家/地区,保证YearMonth 涵盖的每个月每年都会有一行,然后您可以按国家/地区划分并使用ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 对它们求和。

      WITH Grouped AS
      ( SELECT  SUM(Value) AS MonthValue,
                        Year,
                        Period,
                        Country
               FROM     Table1
               GROUP BY Year,
                        Period,
                        Country ), Summed AS
      ( SELECT    YM.*,
                            C.Country,
                            G.Year AS GYear,
                            [3M Value] = SUM(MonthValue) OVER (partition BY C.Country 
                                                         ORDER BY YM.Year, YM.Month 
                                                         ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
                 FROM       YearMonth YM
                 CROSS JOIN Country C
                 LEFT JOIN  Grouped G
                 ON         G.Year = YM.Year
                            AND G.Period = YM.Month
                            AND G.Country = C.Country )
      SELECT   *
      FROM     Summed
      WHERE    GYear IS NOT NULL
      ORDER BY Year,
               Month,
               Country
      

      或具有相同语义但可能更高效的版本

      SELECT      CA.*
      FROM        Country C
      CROSS APPLY
      (
      SELECT    YM.*,
                C.Country,
                G.Year AS GYear,
                [3M Value] = SUM(MonthValue) OVER ( ORDER BY YM.Year, YM.Month 
                                                    ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
      FROM      YearMonth YM
      LEFT JOIN 
      (
      SELECT SUM(Value) AS MonthValue,
             Year,
             Period
      FROM   Table1 T
      WHERE T.Country = C.Country
      GROUP  BY Year,
                Period) G
      ON        G.Year = YM.Year
                AND G.Period = YM.Month
                           ) CA
       WHERE   GYear IS NOT NULL
      ORDER BY    Year,
                  Month
      

      【讨论】:

      • ROWS BETWEEN 3 PRECEDING AND CURRENT ROW 将考虑前 3 行,即使月份缺失。请在演示中添加(2017, 1, 'Elbonia', 100) 行并查看结果。
      • @vkp - 这就是为什么在没有丢失月份的表上有一个外连接。这和分组确保在所涵盖的期间每个月都有一行。
      • 确定 Martin .. 但你只是交叉加入国家之间的全年月份组合,这将省略一些缺失的月份。希望你明白我的意思。
      • @vkp - 不抱歉。我已将您想要添加到演示中的行添加。你认为结果有什么问题? rextester.com/BKKM67176
      • 啊它应该是前 2 行 + 当前行 3 个月。是这样吗?
      猜你喜欢
      • 2022-08-02
      • 1970-01-01
      • 1970-01-01
      • 2021-02-11
      • 1970-01-01
      • 1970-01-01
      • 2019-01-04
      • 1970-01-01
      • 2020-02-11
      相关资源
      最近更新 更多