滚动总和前3个月的SQL Server

时间:2017-01-21 13:54:27

标签: sql sql-server

我有以下表结构:

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)

3 个答案:

答案 0 :(得分:3)

使用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 

答案 1 :(得分: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;

答案 2 :(得分:2)

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

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