sql - 按月和季度分组并汇总前 11 个月

标签 sql sql-server

你好,我已经为此苦苦挣扎了大约一天:/。我想要完成的是在同一个查询中返回月份值和该季度之前 11 个月的值。

所以我有一张 table

CREATE TABLE Test
(
    Id INT,
    Date DATETIME2,
    Value DECIMAL(15,4)
)

我正在尝试获取如下数据:

          This Month   Previous_11_Months
January     100        1100
February    123        1123
March       211        2123

我的实际Sql:

DECLARE @endDate DATETIME2 = '6-30-2017',
    @plantId INT       = 1
DECLARE @endDateMinusYear DATETIME2 = DATEADD(YEAR,-1,@endDate),
        @firstDayOfThisQuarter DATETIME2,
        @firstDayOfThisQuarterLastYear DATETIME2

SELECT @firstDayOfThisQuarter = DATEADD(QQ, DATEDIFF(QQ , 0, @endDate), 0)
SELECT @firstDayOfThisQuarterLastYear = DATEADD(QQ, DATEDIFF(QQ , 0, @endDateMinusYear), 0)


SELECT
    DATENAME(MONTH, PlantPollutions.Date) AS MONTH,
    SUM(PlantPollutions.BurnerNaturalGas + PlantPollutions.OilHeaterNaturalGas) AS THIS_MONTH,
    (
    SELECT
            SUM(SubPlantPollutions.BurnerNaturalGas + SubPlantPollutions.OilHeaterNaturalGas)
        FROM PlantPollutions AS SubPlantPollutions
        WHERE SubPlantPollutions.Date >= DATEADD(MONTH, -11, PlantPollutions.Date)
        GROUP BY SubPlantPollutions.Date
    ) AS PREVIOUS_11_MONTHS
FROM PlantPollutions

WHERE PlantPollutions.PlantId = @plantId
  AND PlantPollutions.Date >= @firstDayOfThisQuarter

GROUP BY DATENAME(MONTH, PlantPollutions.Date)

当我在子查询中注释掉WHERE SubPlantPollutions.Date >= DATEADD(MONTH, -11, PlantPollutions.Date) GROUP BY SubPlantPollutions.Date 它有效

最佳答案

窗口函数通常用于获取上一行或下一行值,例如使用 lead()lag(),或者聚合 的值分区,或按指定的顺序对行进行排名(row_number()rank()dense_rank() )。这利用了 12 行(前 11 行和当前行)的特定窗口框架,并带有以下代码段:sum(ThisMonth) over (order by Month rows 11 previous)

窗口函数作用于查询的结果集,这就是使用按月聚合的派生表的原因。由于您只需要给定季度的行,因此再次嵌套查询(通过将前两个放在 common table expression (cte) 中),以便当 sum 时,您不需要的行中的值仍包含在窗口框架中() 进行计算。

有关窗口函数,特别是窗口框架的更多信息: Window Functions in SQL Server: Part 2-The Frame - Fabiano Amorim

更新问题的更新代码:

declare @EndDate datetime2(0) = '20170630';
;with cte as (
  select
      Month
    , ThisMonth
    , Previous_11_Months = sum(ThisMonth) over (order by Month rows 11 preceding)
  from (
    select 
        Month = dateadd(month, datediff(month, 0, t.Date), 0) -- truncate date to month
      , ThisMonth = sum(Value)
    from dbo.Test t
    where t.Date >= dateadd(year, -1, dateadd(quarter, datediff(quarter, 0, @EndDate), 0))
    group by dateadd(month, datediff(month, 0, t.Date), 0) -- truncate date to month
    ) s
 )
 select 
     MonthName = datename(Month,Month)
   , ThisMonth
   , Previous_11_Months
 from cte
 where month >= dateadd(quarter, datediff(quarter, 0, @EndDate), 0)

rextester 演示:http://rextester.com/OBVR79198

+-----------+-----------+--------------------+
| MonthName | ThisMonth | Previous_11_Months |
+-----------+-----------+--------------------+
| April     |       218 |               2291 |
| May       |       202 |               2312 |
| June      |       189 |               2357 |
| July      |       207 |               2335 |
+-----------+-----------+--------------------+

在 SQL Server 2012+ 中,您可以使用窗口函数对当前月份和之前 11 个月进行求和,并使用派生表/子查询/cte 对 ValueMonth 求和> 像这样:

select
    Month = convert(char(7),Month,120)
  , ThisMonth
  , RollingSum = sum(ThisMonth) over (order by Month rows 11 preceding)
from (
  select 
      Month = dateadd(month, datediff(month, 0, t.Date), 0) -- truncate date to month
    , ThisMonth = sum(Value)
  from dbo.Test t
  group by dateadd(month, datediff(month, 0, t.Date), 0) -- truncate date to month
  ) s

rextester 演示:http://rextester.com/UTLDP89935

返回:

+---------+-----------+------------+
|  Month  | ThisMonth | RollingSum |
+---------+-----------+------------+
| 2015-07 |       214 |        214 |
| 2015-08 |       195 |        409 |
| 2015-09 |       182 |        591 |
| 2015-10 |       168 |        759 |
| 2015-11 |       185 |        944 |
| 2015-12 |       152 |       1096 |
| 2016-01 |       165 |       1261 |
| 2016-02 |       186 |       1447 |
| 2016-03 |       212 |       1659 |
| 2016-04 |       232 |       1891 |
| 2016-05 |       193 |       2084 |
| 2016-06 |       168 |       2252 |
| 2016-07 |       174 |       2212 |
| 2016-08 |       213 |       2230 |
| 2016-09 |       195 |       2243 |
| 2016-10 |       217 |       2292 |
| 2016-11 |       200 |       2307 |
| 2016-12 |       200 |       2355 |
| 2017-01 |       225 |       2415 |
| 2017-02 |       202 |       2431 |
| 2017-03 |       192 |       2411 |
| 2017-04 |       175 |       2354 |
| 2017-05 |       220 |       2381 |
| 2017-06 |       186 |       2399 |
| 2017-07 |       205 |       2430 |
+---------+-----------+------------+

关于sql - 按月和季度分组并汇总前 11 个月,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45399595/

相关文章:

sql-server - 总结区间数据的最佳方法是什么?

mysql - 仅允许具有标识符的实体将实体绑定(bind)到查询参数

sql - 如何更改查询中的行信息?

sql - 为什么我们不能从sql中调用过程

php - 制作事件日志的最有效方法

SQLServer 2008 通用列类型

sql - 在 Access 中选择和计数行的最佳方法

sql-server - 在 Visual Studio 2010 DBML 设计器 (MS SQL/ASP.NET MVC) 中默认设置 UpdateCheck.Never 模式

sql-server - 如何在 mssql(node.js) 中使用 Like 运算符

mysql - MsSQL 到 MySQL 的迁移