SQL Query用于日期值的运行聚合 - 涉及SUM和GROUP BY子句的365天

时间:2013-11-26 16:05:52

标签: sql reporting-services sql-server-2012 aggregate-functions

设置:SQL Server 2012,其中使用Report Builder 3.0在SSRS的折线图中使用数据

我有一张包含销售数据的表格,第一笔交易是在2010年10月10日。编写此表以定期更新销售信息,并将继续写入未来(当前日期为11/26/13,这就是样本表停在那里的原因)。

以下是我开始的原始数据示例:

StartDate          Product         PaidQty          UnPaidQty
-------------------------------------------------------------
2012-10-10         Product A       100              150
2012-10-10         Product B       110              50
2012-10-10         Product C       10               100
2012-10-11         Product A       120              200
2012-10-11         Product D       140              230
2012-10-11         Product E       180              20
...
2013-05-01         Product H       120              60
2013-05-01         Product J       90               90
2013-05-01         Product K       120              160
...
2013-11-25         Product B       90               80
2013-11-25         Product F       190              180
2013-11-25         Product G       120              60

注意:每个日期可能没有值。例如,12/25/12没有一行。

我想要结束的应该是这样的:

StartDate          DailyTotal      AnnualTotal
--------------------------------------------
2012-10-10         520             520       <-- The Sum of 10/10/12 through 10/10/12
2012-10-11         890             1410      <-- The Sum of 10/10/12 through 10/11/12
...
2013-05-01         640             278,000   <-- The Sum of 10/10/12 through 05/01/13
...
2013-11-25         720             450,500   <-- The Sum of 11/26/12 through 11/25/13

获取每日总计列,结合PaidQty和UnPaidQty非常简单:

SELECT StartDate, SUM(PaidQty) + SUM(UnPaidQty) AS Total
FROM Table
GROUP BY StartDate
ORDER BY StartDate

但是,我需要最终提供的数据为我提供每日总计(付费+未付费)以及之前365天的总计。因此,因为在10/10/2012之前没有数据,所以运行总计将从2010年10月10日到2013年11月10日之间的总和相加,此时它将总计StartDate值的总和 - 365天。有大量的产品清单,其中任何一种都可以在任何一天销售,但对于我的最终结果,我不关心产品。使用GROUP BY子句可以使用此信息,以便为每个日期返回单行。

我只是没有抓住我需要做的事情才能为正在运行的年度总数添加额外的列。我尝试过使用OVER()子句,例如:

SELECT StartDate, SUM(PaidQty) + SUM(UnPaidQty) AS Total, SUM(PaidQty) + SUM(UnPaidQty) OVER (ORDER BY StartDate) AS AnnualTotal
FROM Table
GROUP BY StartDate
ORDER BY StartDate 

但是这会导致PaidQty和UnPaidQty需要在聚合函数或GROUP BY子句中的错误。如果我将这些添加到GROUP BY子句中,那么我最终会为每个日期重复多行,并且运行值不正确。

编辑:正如下面Aaron的回答所示,我最终得到了以下问题:

    IF OBJECT_ID('tempdb..#x') IS NOT NULL DROP TABLE #x 
CREATE TABLE #x
(
    StartDate   DATETIME,
    PaidQty     INT,
    UnPaidQty   INT
)
INSERT INTO #x
SELECT StartDate, SUM(PaidQty), SUM(UnPaidQty)
FROM MyTable
GROUP BY StartDate

SELECT Date, PaidQty+UnPaidQty AS DailyTotal, 
            SUM(PaidQty+UnPaidQty) OVER (Order By Date ROWS 364 PRECEDING) AS AnnualTotal
FROM CalendarDates
LEFT JOIN #x ON Date = StartDate
WHERE Date BETWEEN '2012-10-10' AND GetDate()
ORDER BY Date

我在我的数据库中创建了一个名为CalendarDates的表,它只包含从2010年1月1日到12月31日的所有日期的列表。这用于为没有完成销售的任何日期填写NULL。

1 个答案:

答案 0 :(得分:3)

DECLARE @x TABLE(StartDate DATE, Product VARCHAR(30), PaidQty INT, UnPaidQty INT);

INSERT @x VALUES
('2012-10-10','Product A',100,150),
('2012-10-10','Product B',110,50 ),
('2012-10-10','Product C',10 ,100),
('2012-10-11','Product A',120,200),
('2012-10-11','Product D',140,230),
('2012-10-11','Product E',180,20 ),
('2012-10-12','Product B',90 ,80 ),
('2012-10-12','Product F',190,180),
('2012-10-12','Product G',120,60 );

;WITH x AS
(
  SELECT StartDate, pq = SUM(PaidQty), uq = SUM(UnPaidQty)
  FROM @x GROUP BY StartDate
)
SELECT StartDate, pq, uq,
  SUM(pq+uq) OVER (ORDER BY StartDate ROWS 365 PRECEDING)
FROM x;

现在,假设每个日期都有一行。如果没有,您可能希望生成一组日期并将其用作左连接中的锚点。否则,之前的365行将代表超过365天。要做到这一点:

DECLARE @minDate DATE, @maxDate DATE, @delta INT;

SELECT @maxDate = MAX(StartDate), @minDate = MIN(StartDate) FROM @x;

SET @delta = DATEDIFF(DAY, @minDate, @maxDate);

IF @delta > 364
  SELECT @minDate = DATEADD(DAY, -364, @maxDate), @delta = 364;

;WITH n(n) AS
(
  SELECT TOP (@delta+1) ROW_NUMBER() OVER (ORDER BY [object_id])
  FROM sys.all_columns
),
d(d) AS (SELECT DATEADD(DAY, n-1, @minDate) FROM n),
x AS 
(
  SELECT StartDate = d.d, pq = SUM(PaidQty), uq = SUM(UnPaidQty)
    FROM d LEFT OUTER JOIN @x AS x 
    ON d.d = x.StartDate GROUP BY d.d
)
SELECT StartDate, pq, uq,
  SUM(pq+uq) OVER (ORDER BY StartDate ROWS 365 PRECEDING)
FROM x
ORDER BY StartDate;