简体   繁体   English

sql 服务器滚动 12 个月总和有日期间隔

[英]sql server rolling 12 months sum with date gaps

Suppose I have a table that indicates the number of items sold in a particular month for each sales rep.假设我有一个表,显示每个销售代表在特定月份售出的商品数量。 However, there will not be a row for a particular person in months where there were no sales.但是,在没有销售的月份里,不会有特定人员的争吵。 Example例子

rep_id     month_yr     num_sales    
1          01/01/2012    3    
1          05/01/2012    1    
1          11/01/2012    1    
2          02/01/2012    2    
2          05/01/2012    1  

I want to be able to create a query that shows for each rep_id and all possible months (01/01/2012, 02/01/2012, etc. through current) a rolling 12 month sales sum, like this:我希望能够创建一个查询,显示每个 rep_id 和所有可能的月份(01/01/2012、02/01/2012 等到当前)滚动 12 个月的销售额,如下所示:

rep_id     month_yr     R12_Sum    
1          11/01/2012   5    
1          12/01/2012   5    
1          01/01/2013   5    
1          02/01/2013   2

I have found some examples online, but the problem I'm running into is I'm missing some dates for each rep_id.我在网上找到了一些示例,但我遇到的问题是我缺少每个 rep_id 的一些日期。 Do I need to cross join or something?我需要交叉连接还是什么?

To solve this problem, you need a driver table that has all year/month combinations. 要解决此问题,您需要一个具有所有年/月组合的驱动程序表。 Then, you need to create this for each rep. 然后,您需要为每个代表创建此项。

The solution is then to left join the actual data to this driver and aggregate the period that you want. 然后解决方案是将实际数据连接到此驱动程序并聚合所需的时间段。 Here is the query: 这是查询:

with months as (
    select 1 as mon union all select 2 union all select 3 union all select 4 union all
    select 5 as mon union all select 6 union all select 7 union all select 8 union all
    select 9 as mon union all select 10 union all select 11 union all select 12
   ),
    years as (select 2010 as yr union all select 2011 union all select 2012 union all select 2013
   ),
    monthyears as (
     select yr, mon, yr*12+mon as yrmon
     from months cross join years
    ),
     rmy as (
     select *
     from monthyears my cross join
          (select distinct rep_id from t
          ) r
    )
select rmy.rep_id, rmy.yr, rmy.mon, SUM(t.num_sales) as r12_sum
from rmy join
     t
     on rmy.rep_id = t.rep_id and
        t.year(month_yr)*12 + month(month_yr) between rmy.yrmon - 11 and rmy.yrmon
group by rmy.rep_id, rmy.yr, rmy.mon
order by 1, 2, 3  

This hasn't been tested, so it may have syntactic errors. 这尚未经过测试,因此可能存在语法错误。 Also, it doesn't convert the year/month combination back to a date, leaving the values in separate columns. 此外,它不会将年/月组合转换回日期,而是将值保留在单独的列中。

Here is one solution: 这是一个解决方案:

SELECT 
  a.rep_id
  ,a.month_yr
  ,SUM(b.R12_Sum) AS R12_TTM
FROM YourTable a
  LEFT OUTER JOIN YourTable b 
    ON a.rep_id = b.rep_id
    AND a.month_yr <= b.month_yr
    AND a.month_yr >= DATEADD(MONTH, -11, b.month_yr)
GROUP BY
  a.rep_id
  ,a.month_yr

It's certainly not pretty but is more simple than a CTE, numbers table or self join: 它肯定不漂亮,但比CTE,数字表或自联接更简单:

DECLARE @startdt DATETIME

SET @startdt = '2012-01-01'

SELECT rep_id, YEAR(month_yr), MONTH(month_yr), SUM(num_sales)
FROM MyTable WHERE month_yr >= @startdt AND month_yr < DATEADD(MONTH,1,@startdt)

UNION ALL

SELECT rep_id, YEAR(month_yr), MONTH(month_yr), SUM(num_sales)
FROM MyTable WHERE month_yr >= DATEADD(MONTH,1,@startdt) AND month_yr < DATEADD(MONTH,2,@startdt)

UNION ALL

SELECT rep_id, YEAR(month_yr), MONTH(month_yr), SUM(num_sales)
FROM MyTable WHERE month_yr >= DATEADD(MONTH,2,@startdt) AND month_yr < DATEADD(MONTH,3,@startdt)

UNION ALL

SELECT rep_id, YEAR(month_yr), MONTH(month_yr), SUM(num_sales)
FROM MyTable WHERE month_yr >= DATEADD(MONTH,3,@startdt) AND month_yr < DATEADD(MONTH,4,@startdt)

UNION ALL

etc etc

The following demonstrates using a CTE to generate a table of dates and generating a summary report using the CTE. 以下演示使用CTE生成日期表并使用CTE生成摘要报告。 Sales representatives are omitted from the results when they have had no applicable sales. 如果销售代表没有适用的销售,则会从结果中省略销售代表。

Try jiggling the reporting parameters, eg setting @RollingMonths to 1 , for more entertainment. 尝试@RollingMonths报告参数,例如将@RollingMonths设置为1 ,以获得更多娱乐。

-- Sample data.
declare @Sales as Table ( rep_id Int, month_yr Date, num_sales Int );
insert into @Sales ( rep_id, month_yr, num_sales ) values
  ( 1, '01/01/2012', 3 ),
  ( 1, '05/01/2012', 1 ),
  ( 1, '11/01/2012', 1 ),
  ( 2, '02/01/2012', 1 ),
  ( 2, '05/01/2012', 2 );
select * from @Sales;

-- Reporting parameters.
declare @ReportEnd as Date = DateAdd( day, 1 - Day( GetDate() ), GetDate() ); -- The first of the current month.
declare @ReportMonths as Int = 6; -- Number of months to report.
declare @RollingMonths as Int = 12; -- Number of months in rolling sums.

-- Report.
--   A CTE generates a table of month/year combinations covering the desired reporting time period.
with ReportingIntervals as (
  select DateAdd( month, 1 - @ReportMonths, @ReportEnd ) as ReportingInterval,
    DateAdd( month, 1 - @RollingMonths, DateAdd( month, 1 - @ReportMonths, @ReportEnd ) ) as FirstRollingMonth
  union all
  select DateAdd( month, 1, ReportingInterval ), DateAdd( month, 1, FirstRollingMonth )
    from ReportingIntervals
    where ReportingInterval < @ReportEnd )
  -- Join the CTE with the sample data and summarize.
  select RI.ReportingInterval, S.rep_id, Sum( S.num_sales ) as R12_Sum
    from ReportingIntervals as RI left outer join
      @Sales as S on RI.FirstRollingMonth <= S.month_yr and S.month_yr <= RI.ReportingInterval
    group by RI.ReportingInterval, S.rep_id
    order by RI.ReportingInterval, S.rep_id

It is pretty simple using a join with 2 conditions.使用具有 2 个条件的连接非常简单。 Consider 2 years of data (2022-2023) for every rep with no sales in some months.考虑 2 年的数据 (2022-2023),每个代表在某些月份没有销售。

Aggregate at rep, month level in the following manner.按以下方式在代表、月份级别汇总。

Let's consider May 2023. Apply 2 conditions:让我们考虑 2023 年 5 月。应用 2 个条件:

  1. Join only previous or current months, not future (Nothing after May 2023 which leaves you with Jan 2022 to May 2023)仅加入之前或当前的月份,而不是未来的月份(2023 年 5 月之后没有任何内容,这让您只剩下 2022 年 1 月到 2023 年 5 月)

  2. Take only those months that come after Last Year's May (which leaves you with June 2022 to May 2023)只考虑去年五月之后的那几个月(剩下的是 2022 年 6 月到 2023 年 5 月)

In this manner, even if Sept 2022 is missing, it won't matter to you.这样,即使缺少 2022 年 9 月,对您来说也无所谓。 Hope this helps!希望这可以帮助!

SELECT 
  a.rep_id,
  a.month_yr,
  SUM(b.R12_Sum) AS R12_TTM

FROM YourTable a
LEFT JOIN YourTable b 
     ON a.rep_id = b.rep_id
        AND a.month_yr >= b.month_yr
        AND DATEADD(MONTH, -11, a.month_yr) <= b.month_yr

GROUP BY a.rep_id, a.month_yr

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM