我有以下表结构:
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)
答案 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