【问题标题】:Creating a query to aggregate 3 Month, 6 Month totals创建查询以聚合 3 个月、6 个月的总计
【发布时间】:2016-06-27 03:19:10
【问题描述】:

我正在使用 SQL Server 2008 R2。我正在尝试创建一个按 3M、6M、9M、12M 总计汇总总计的查询。以下是一些示例数据:

create table invoice (store int, invoice_date datetime, customer int, is_repeat varchar(1));

insert into invoice (store, invoice_date, customer, is_repeat)
values (1, '2015-10-05', 1, 'N'),
(1, '2016-03-04', 1, 'Y'),
(1, '2016-02-07', 1, 'Y'),
(1, '2015-08-03', 2, 'Y'),
(2, '2015-12-01', 3, 'Y'),
(2, '2016-02-16', 4, 'Y'),
(2, '2015-06-11', 3, 'Y'),
(2, '2015-09-18', 4, 'Y');

查询需要给出 3M、6M 等“Y”值的计数,并按商店、月份和年份进行聚合。例如,2016 年 3 月的商店 1 应该有一条记录,该记录总计 2015 年 12 月 1 日和 2016 年 2 月 29 日之间的“Y”值的数量,并将该值放入 3M 列。然后将 2015 年 9 月 1 日和 2016 年 2 月 29 日之间的值相加,并将这些值放在 6M 列中。

以下是上述数据的查询结果示例:

STORE     MONTH     YEAR     3M_REPEATS     6M_REPEATS
-----     -----     ----     ----------     ----------
  1         2       2016         0              2
  1         3       2016         1              2
  2         2       2016         1              3
  2         3       2016         2              3

我正在尝试寻找一个优雅的解决方案,但我不确定是否应该使用 CTE、PIVOT 或 OVER(PARTITION BY),还是只使用标准的 GROUP BY 子句。

编辑添加:我最终只需要此表中的最后 12 个月,因此此表中只有 12 条记录。例如,如果当前月份/年份是 3/2016,则它将具有 2/2015 - 3/2016 以及每个月的总数。

【问题讨论】:

  • 如果一个月内没有任何记录(例如,您的样本中为 2015 年 11 月),是否还需要在结果中显示为一行?
  • @Ross Presser 是的,对于给定的商店、月份和年份,如果在过去 3 个月/6 个月内没有 is_repeat = 'Y' 的记录,我仍然希望看到该商店/月/年的记录,但 3M 和 6M 的总数为 0。在我的商店 2 示例数据中,由于 2015-09-18 和 2015-06-11 invoice_date 值,2015 年 11 月的结果行将具有 3M_REPEATS = 1 和 6M_REPEATS = 2。我希望这能回答您的问题,并感谢您的帮助。

标签: sql sql-server sql-server-2008-r2 aggregation


【解决方案1】:

使用 case 语句对每个季度的记录求和,以获取每条记录的季度,然后使用 group by

select 
store,
datepart(m, invoice_date) as mth,
datepart(yyyy, invoice_date) as yr,
Sum(case when datename(qq,invoice_date)=1 then 1 else 0 end) as QTR1, 
Sum(case when datename(qq,invoice_date)=2 6 then 1 else 0 end) as QTR2
FROM test 
WHERE is_repeat='Y'
GROUP BY store, datepart(m, t.invoice_date), datepart(yyyy, t.invoice_date)
ORDER BY store, mth, yr

【讨论】:

    【解决方案2】:

    我会为聚合和 GROUP BY Store、Month、Year 的 SUM CASE 表达式。

    【讨论】:

      【解决方案3】:

      这是一个使用分析函数的版本:

      with agg as (
          select
              store,
              min(invoice_date) as invoice_month,
              count(case when is_repeat = 'Y' then 1 end) as y_count 
          from invoice
          where 
                  invoice_date >= dateadd(month, -14, current_timestamp)
              and datediff(month, invoice_date, current_timestamp) between 1 and 12 
          group by
              store,
              datediff(month, invoice_date, current_timestamp)
      )
      select
          store,
          datepart(month, a1.invoice_month) as "month",
          datepart(year, a1.invoice_month) as "year",
          sum(y_count) over (
              partition by store
              order by invoice_month
              rows between 2 preceding and current row
          ) as 3m_repeats,
          sum(y_count) over (
              partition by store
              order by invoice_month
              rows between 5 preceding and current row
          ) as 6m_repeats
      from agg
      

      但由于您不能在 2008 年使用 rows between,我认为这可以作为替代:

      with agg as (
          select
              store,
              min(invoice_date) as invoice_month,
              count(case when is_repeat = 'Y' then 1 end) as y_count 
          from invoice
          where 
                  invoice_date >= dateadd(month, -14, current_timestamp)
              and datediff(month, invoice_date, current_timestamp) between 1 and 12 
          group by
              store,
              datediff(month, invoice_date, current_timestamp)
      )
      select
          store,
          datepart(month, a1.invoice_month) as "month",
          datepart(year, a1.invoice_month) as "year",
          sum(case
              when datediff(month, a1.invoice_date, a2.invoice_date)
                  between -2 and 0
              then y_count end
          ) as 3m_repeats,
          sum(case
              when datediff(month, a1.invoice_date, a2.invoice_date)
                  between -5 and 0 then y_count end
          ) as 6m_repeats,
      from agg a1 inner join agg a2
          on      a2.store = a1.store
              and datediff(month, a1.invoice_date, a2.invoice_date) between -5 and 0
      group by a1.store, a1.invoice_month
      

      我假设你有所有月份的数据,所以我没有为这种复杂性而烦恼。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多