前 3 个月的滚动总和 SQL Server
Posted
技术标签:
【中文标题】前 3 个月的滚动总和 SQL Server【英文标题】:Rolling sum previous 3 months 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)
【问题讨论】:
【参考方案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
Live Demo
【讨论】:
【参考方案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
(就此而言,JOIN
和 ON
)不是主要子句(想想SELECT
、FROM
、WHERE
、GROUP 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 个月。是这样吗?以上是关于前 3 个月的滚动总和 SQL Server的主要内容,如果未能解决你的问题,请参考以下文章