如何获得最近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