2

問題:對於包含日期列,任意數量的類別列和值列的給定記錄集,我想計算任意日期窗口的值的聚合,例如30天,365天等等。我已經看過窗口集合函數,CTE和其他一些函數,但它們並沒有出現(至少對我來說)來執行所需的功能。SQL移動聚合

下面的SQL(T-SQL)代表了我試圖完成的基本概念,但是我對它的可伸縮性,特別是連接有不好的感覺,並且一旦我嘗試按其他名義組進行分組,就會遇到困難。

SELECT 
     basedate 
     , count(*) as [n] 
     , sum(Value) as [SumValue] 
     , avg(value) As [AverageValue] 
     , stdev(value) As [StdevValue] 
FROM 
    (SELECT t1.basedate , t2.* 
    FROM 
     (SELECT DISTINCT dt as basedate from foo)as t1 
     ,foo as t2 
     WHERE datediff(d, t1.basedate, t2.dt) between -30 and 0 
    ) t3 
GROUP BY t3.basedate 
ORDER BY t3.BASEDATE DESC 

我創建了一個SQLFiddle來試圖使它更加具體。

SQLFiddle

謝謝。

+0

您是否使用SQL Server 2008作爲你的小提琴建議? – 2015-02-24 17:45:50

+0

我目前正在使用SQL-Server 2008,但這可能是一條Teradata查詢。 – user3092841 2015-02-24 17:47:43

回答

0

在SqlFiddle中提供的設置中玩了一番之後,我來到了這兩個潛在的解決方案:(好吧,第一個解決方案只有一半,不知道如何將stdev()添加到高效路)

WITH t1 
    AS (SELECT DISTINCT dt as basedate from foo), 
    sumcount 
    AS (SELECT basedate, 
      SUM((CASE WHEN datediff(d, t1.basedate, t2.dt) between -30 and 0 THEN 1 ELSE 0 END)) as [n], 
      SUM((CASE WHEN datediff(d, t1.basedate, t2.dt) between -30 and 0 THEN value ELSE 0 END)) as [Sumvalue] 
     FROM t1, foo t2 
     GROUP BY basedate) 
SELECT basedate, 
     [n], 
     [Sumvalue], 
     [Sumvalue]/[n] as [Averagevalue] 
    FROM sumcount 
ORDER BY basedate DESC 


GO 

WITH t1 
    AS (SELECT DISTINCT dt as basedate from foo), 
    t2 
    AS (SELECT basedate, min_date = DateAdd(day, -30, basedate), max_date = DateAdd(day, 0, basedate) from t1) 

SELECT basedate, 
      count(*) as [n] 
     , sum(b.value) as [Sumvalue] 
     , avg(b.value) As [Averagevalue] 
     , stdev(b.value) As [Stdevvalue] 
FROM t2 
JOIN foo b 
    ON b.dt BETWEEN t2.min_date AND t2.max_date 
GROUP BY basedate 
ORDER BY basedate DESC 

我喜歡最後一個爲它的簡單可讀性,並且巧合的運行相當快一點過,雖然我不能完全知道爲什麼呢。請注意,我將測試數據加載了100次(使用GO 100的魔法),以便在筆記本電腦上獲得更長的持續時間。 (這是很難比較1毫秒VS 1毫秒=)

Query Plan Explorer Screenshot

令人驚訝的在(接受)的解決方案從暫停CO 比原來的查詢返回不同的結果(或 '我' 查詢)時給定'擴展'測試集;你可能想看看! (原因在於它多次查找基準日期,因此導致了多次累加,然後最終得到了更大的Counts和SumValues。我不確定這是你想要的,還是它是某種東西這可能發生在'真實數據',但由於你把一個索引,而不是一個獨特的索引,我假設雙打可以發生......)

0

在我簡短的測試,這是比你當前的查詢速度更快,如果dt字段建立索引:

SELECT a.dt AS basedate 
     , count(*) as [n] 
     , sum(b.Value) as [SumValue] 
     , avg(b.value) As [AverageValue] 
     , stdev(b.value) As [StdevValue] 
FROM foo a 
JOIN foo b 
    ON b.dt BETWEEN DATEADD(DAY,-30,a.dt) AND a.dt 
GROUP BY a.dt 
ORDER BY a.dt DESC 

編輯:我是因爲在SQL Server被問及版2012+有一個爲RANGE/ROWS支持,可以創建一個你喜歡的移動窗口,我相信你會陷入一種自我加入。使用DATEADD()並比較dt的值稍微快於DATEDIFF()版本。

+0

我添加了索引並在本地進行了測試。原始解決方案:1108ms CPU,179ms已過。建議的解決方案:1841毫秒CPU,512毫秒已過。在原始解決方案foo表上的IO:掃描計數12,邏輯讀取178,工作表掃描計數8,邏輯讀取66862,提議的解決方案:foo掃描計數10,邏輯讀取31,工作表掃描計數836邏輯讀取244163。考慮到io結果,更具可擴展性的解決方案? – user3092841 2015-02-24 19:05:52

+0

@ user3092841原始查詢是否已被緩存?我看到的性能提高的原因是,由於在比較中使用了'dt'而不是'DATEDIFF()',所以我得到了索引查找而不是索引掃描,但是無論哪種方式,您都會失去一些好處,因爲您需要一個範圍。 – 2015-02-24 19:09:02

+0

我更新了SQL小提琴[鏈接](http://www.sqlfiddle.com/#!3/2ba2f4/4)計劃幾乎相同。 [鏈接](http://www.sqlfiddle.com/#!3/2ba2f4/4/0)VS [鏈接](http://www.sqlfiddle.com/#!3/2ba2f4/4/1)I非常感謝你對此的幫助,我想也許有一個數量級的解決方案,但似乎沒有。 – user3092841 2015-02-24 19:23:15