如何获得最近x年的总和
How to get sum for the last x year
我们使用 CoID、PeriodType(M 代表月份,Q 代表季度)、FinDate(月底日期)和 Sales 进行 table 测试,如下所示:
CREATE TABLE Test(
[CoID] [int] NOT NULL,
[PeriodType] [varchar](50) NOT NULL,
[FinDate] [datetime] NOT NULL,
[Sales] [float] NOT NULL
)
我们需要获得过去 x 年的销售额。
如果 table 中没有重复数据(如果有 10/31/18 的 PeriodType = "M" 的数据,则不会有数据
对于 12/31/19,PeriodType = "Q"),我可以使用以下查询获取过去 1 年的销售额
SELECT SUM(SALES) FROM TEST WHERE FINDATE >= EOMONTH(getdate(),-12) GROUP BY COID
如果table中有重复数据(有10/31/18的数据,PeriodType = "M",但也有12/31/19的数据
PeriodType = "Q",查询应该是什么样的?
另外,如果没有去年1个月的数据,我想return0,比如获取最近1年的销售数据,但是
没有 11/30/18 的数据,那么我想 return 0。查询应该如何显示?
CoID PeriodType FinDate Sales
1 M 9/30/18 11
1 M 10/31/18 10
1 M 11/30/18 10
1 M 12/31/18 10
1 Q 3/31/19 10
1 Q 6/30/19 10
1 M 7/31/19 10
1 M 8/31/19 10
1 M 9/30/19 10
2 M 10/31/18 11
2 M 11/30/18 12
2 M 12/31/18 13
2 Q 3/31/19 14
2 M 4/30/19 15
2 M 5/31/19 16
2 M 6/30/19 17
想要的结果
CoID SumSales
1 80 --> doesn't include sales from 9/30/18 (1 year ago starts from 10/31/18)
2 0 --> missing sales from 7/31/19, 8/31/19 and 9/30/19, so we set sales to 0
--in case you want results from several years, you can create your own calendar.
--If not, you can replace the dates of the 'between' statement in the next query
select EOMONTH(getdate(),-13) as first_day_of_your_year
, EOMONTH(EOMONTH(getdate(),-13), +12) as last_day_of_your_year
, '1' as your_year
into your_years_table;
--add your year to the table
select a.*, b.your_year
into Test2
from Test a join your_years_table b
on (a.FinDate between b.first_day_of_your_year and b.last_day_of_your_year);
-- here I strongly assume that the entire quarter belongs to a single year (the year of the last day of the quarter).
--group by CoID and year
select CoID
, your_year
, sum(case when PeriodType = 'M' then 1 else 3 end) as count_months
, Sum(Sales) as total_sales
into Test3
from Test2
where your_year is not null
group by CoID, your_year;
--if there is no data for 1 month in the last year, you get 0
select CoID
, your_year
, case when count_months = 12 then total_sales else 0 end as sum_sales
into Test4
from Test3
where your_year = '1';
这似乎适用于示例数据。
内联评论。
declare @table table (CoID int, PeriodType varchar(1), FinDate date, Sales int)
insert into @table
values (1, 'M' ,'9/30/18' ,11),
(1, 'M' ,'10/31/18',10),
(1, 'M' ,'11/30/18',10),
(1, 'M' ,'12/31/18',10),
(1, 'Q' ,'3/31/19' ,10),
(1, 'Q' ,'6/30/19' ,10),
(1, 'M' ,'7/31/19' ,10),
(1, 'M' ,'8/31/19' ,10),
(1, 'M' ,'9/30/19' ,10),
(2, 'M' ,'10/31/18',11),
(2, 'M' ,'11/30/18' ,12),
(2, 'M' ,'12/31/18' ,13),
(2, 'Q' ,'3/31/19' ,14),
(2, 'M' ,'4/30/19' ,15),
(2, 'M' ,'5/31/19' ,16),
(2, 'M' ,'6/30/19' ,17)
-- We need a table of months so we can determine if anything is missing.
declare @months table (mnth int)
insert into @months
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
-- This CTE takes the Quarter's sales and turns them into 3 months' sales
;with CTEquarterToMonth as (
-- start with the last month of a quarter
select CoID, month(FinDate) mnth, Sales
from @table t3
where findate >= eomonth(getdate(),-12)
and periodtype = 'Q'
union all
-- this is the 2nd month of the quarter, but only if it is also within 1 year
select CoID, month(dateadd("m",-1,FinDate)) mnth, 0 Sales
from @table t3
where findate >= eomonth(getdate(),-11)
and periodtype = 'Q'
union all
-- this is the 1st month of the quarter, but only if it is also within 1 year
select CoID, month(dateadd("m",-2,FinDate)) mnth, 0 Sales
from @table t3
where findate >= eomonth(getdate(),-10)
and periodtype = 'Q'
),
-- This CTE adds individual months to the months we already have gotten
-- from the Quarters' sales.
CTEcombinedMonths as (
select *
from CTEquarterToMonth
union all
select CoID, month(FinDate) mnth, Sales
from @table t
where findate >= eomonth(getdate(),-12)
and periodtype = 'M'
and not exists (select 1 -- This ignores duplicate months
from CTEquarterToMonth cte
where cte.CoID = t.CoID
and cte.mnth = month(FinDate)
)
)
select months.CoID,
-- The following case statement will set the sales to 0
-- if there are any missing months for a given CoID.
case when sum(case when cte.mnth is null then 1 else 0 end) > 0
then 0
else sum(Sales)
end YearSales
from ( -- derive a table of CoID's and all 12 months
select CoID, mnth
from @months
inner join (
select distinct CoID
from @table
) t on 1=1
) months
-- left join so we can find the missing months
left outer join CTEcombinedMonths cte
on months.CoID = cte.CoID
and months.mnth = cte.mnth
group by months.CoID
受 Michelle 部分回答的启发,我喜欢使用计数而不是 @months table。您可以完全摆脱 @months table 并在所有 CTE 声明之后使用以下查询:
select CoID,
-- The following case statement will set the sales to 0
-- if there are any missing months for a given CoID.
case when count(*) <> 12
then 0
else sum(Sales)
end YearSales
from CTEcombinedMonths cte
group by CoID
我们使用 CoID、PeriodType(M 代表月份,Q 代表季度)、FinDate(月底日期)和 Sales 进行 table 测试,如下所示:
CREATE TABLE Test(
[CoID] [int] NOT NULL,
[PeriodType] [varchar](50) NOT NULL,
[FinDate] [datetime] NOT NULL,
[Sales] [float] NOT NULL
)
我们需要获得过去 x 年的销售额。 如果 table 中没有重复数据(如果有 10/31/18 的 PeriodType = "M" 的数据,则不会有数据 对于 12/31/19,PeriodType = "Q"),我可以使用以下查询获取过去 1 年的销售额
SELECT SUM(SALES) FROM TEST WHERE FINDATE >= EOMONTH(getdate(),-12) GROUP BY COID
如果table中有重复数据(有10/31/18的数据,PeriodType = "M",但也有12/31/19的数据 PeriodType = "Q",查询应该是什么样的?
另外,如果没有去年1个月的数据,我想return0,比如获取最近1年的销售数据,但是 没有 11/30/18 的数据,那么我想 return 0。查询应该如何显示?
CoID PeriodType FinDate Sales
1 M 9/30/18 11
1 M 10/31/18 10
1 M 11/30/18 10
1 M 12/31/18 10
1 Q 3/31/19 10
1 Q 6/30/19 10
1 M 7/31/19 10
1 M 8/31/19 10
1 M 9/30/19 10
2 M 10/31/18 11
2 M 11/30/18 12
2 M 12/31/18 13
2 Q 3/31/19 14
2 M 4/30/19 15
2 M 5/31/19 16
2 M 6/30/19 17
想要的结果
CoID SumSales
1 80 --> doesn't include sales from 9/30/18 (1 year ago starts from 10/31/18)
2 0 --> missing sales from 7/31/19, 8/31/19 and 9/30/19, so we set sales to 0
--in case you want results from several years, you can create your own calendar.
--If not, you can replace the dates of the 'between' statement in the next query
select EOMONTH(getdate(),-13) as first_day_of_your_year
, EOMONTH(EOMONTH(getdate(),-13), +12) as last_day_of_your_year
, '1' as your_year
into your_years_table;
--add your year to the table
select a.*, b.your_year
into Test2
from Test a join your_years_table b
on (a.FinDate between b.first_day_of_your_year and b.last_day_of_your_year);
-- here I strongly assume that the entire quarter belongs to a single year (the year of the last day of the quarter).
--group by CoID and year
select CoID
, your_year
, sum(case when PeriodType = 'M' then 1 else 3 end) as count_months
, Sum(Sales) as total_sales
into Test3
from Test2
where your_year is not null
group by CoID, your_year;
--if there is no data for 1 month in the last year, you get 0
select CoID
, your_year
, case when count_months = 12 then total_sales else 0 end as sum_sales
into Test4
from Test3
where your_year = '1';
这似乎适用于示例数据。
内联评论。
declare @table table (CoID int, PeriodType varchar(1), FinDate date, Sales int)
insert into @table
values (1, 'M' ,'9/30/18' ,11),
(1, 'M' ,'10/31/18',10),
(1, 'M' ,'11/30/18',10),
(1, 'M' ,'12/31/18',10),
(1, 'Q' ,'3/31/19' ,10),
(1, 'Q' ,'6/30/19' ,10),
(1, 'M' ,'7/31/19' ,10),
(1, 'M' ,'8/31/19' ,10),
(1, 'M' ,'9/30/19' ,10),
(2, 'M' ,'10/31/18',11),
(2, 'M' ,'11/30/18' ,12),
(2, 'M' ,'12/31/18' ,13),
(2, 'Q' ,'3/31/19' ,14),
(2, 'M' ,'4/30/19' ,15),
(2, 'M' ,'5/31/19' ,16),
(2, 'M' ,'6/30/19' ,17)
-- We need a table of months so we can determine if anything is missing.
declare @months table (mnth int)
insert into @months
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
-- This CTE takes the Quarter's sales and turns them into 3 months' sales
;with CTEquarterToMonth as (
-- start with the last month of a quarter
select CoID, month(FinDate) mnth, Sales
from @table t3
where findate >= eomonth(getdate(),-12)
and periodtype = 'Q'
union all
-- this is the 2nd month of the quarter, but only if it is also within 1 year
select CoID, month(dateadd("m",-1,FinDate)) mnth, 0 Sales
from @table t3
where findate >= eomonth(getdate(),-11)
and periodtype = 'Q'
union all
-- this is the 1st month of the quarter, but only if it is also within 1 year
select CoID, month(dateadd("m",-2,FinDate)) mnth, 0 Sales
from @table t3
where findate >= eomonth(getdate(),-10)
and periodtype = 'Q'
),
-- This CTE adds individual months to the months we already have gotten
-- from the Quarters' sales.
CTEcombinedMonths as (
select *
from CTEquarterToMonth
union all
select CoID, month(FinDate) mnth, Sales
from @table t
where findate >= eomonth(getdate(),-12)
and periodtype = 'M'
and not exists (select 1 -- This ignores duplicate months
from CTEquarterToMonth cte
where cte.CoID = t.CoID
and cte.mnth = month(FinDate)
)
)
select months.CoID,
-- The following case statement will set the sales to 0
-- if there are any missing months for a given CoID.
case when sum(case when cte.mnth is null then 1 else 0 end) > 0
then 0
else sum(Sales)
end YearSales
from ( -- derive a table of CoID's and all 12 months
select CoID, mnth
from @months
inner join (
select distinct CoID
from @table
) t on 1=1
) months
-- left join so we can find the missing months
left outer join CTEcombinedMonths cte
on months.CoID = cte.CoID
and months.mnth = cte.mnth
group by months.CoID
受 Michelle 部分回答的启发,我喜欢使用计数而不是 @months table。您可以完全摆脱 @months table 并在所有 CTE 声明之后使用以下查询:
select CoID,
-- The following case statement will set the sales to 0
-- if there are any missing months for a given CoID.
case when count(*) <> 12
then 0
else sum(Sales)
end YearSales
from CTEcombinedMonths cte
group by CoID