2012-11-08 161 views
3

我們只是說我有兩列DATE和VALUE。這些日期不一定是有規律的間隔。如何在過去的N天內創建VALUE的移動平均值?我使用postgres,但是mysql解決方案也會有所幫助。如何在SQL中的日期範圍內移動平均值?

數據:

DATE VALUE 
2012-11-05 10 
2012-10-29 31 
2012-10-22 108 
2012-10-17 3654 
2012-10-16 1187 
2012-10-15 12033 
2012-10-09 41 
2012-10-01 85 
2012-09-25 20 
2012-09-24 285 
2012-09-17 20 
2012-09-10 20 
2012-09-04 41 
2012-08-27 63 
2012-08-20 52 
2012-08-13 160 
+0

你是指跑步總數? –

+0

請提供樣本數據和期望的輸出。 – RedFilter

+0

約翰,我的意思是跑平均數。因此,每行應包含過去N天(其中N是任意的)的值的平均值,包括當前日期。 –

回答

7
下面

MySQL的示例覆蓋了滑動7天的窗口:

select t1.`DATE`, AVG(t2.`VALUE`) as MV_AVG 
from MyTable t1 
left outer join MyTable t2 
    on t2.`DATE` between DATE_ADD(t1.`DATE`, INTERVAL -6 DAY) 
     and t1.`DATE` 
group by t1.`DATE` 

SQL Fiddle Example

輸出:

|        DATE | MV_AVG | 
------------------------------------------------ 
| August, 12 2012 20:00:00+0000 |  160 | 
| August, 19 2012 20:00:00+0000 |  52 | 
| August, 26 2012 20:00:00+0000 |  63 | 
| September, 03 2012 20:00:00+0000 |  41 | 
| September, 09 2012 20:00:00+0000 |  30.5 | 
| September, 16 2012 20:00:00+0000 |  20 | 
| September, 23 2012 20:00:00+0000 |  285 | 
| September, 24 2012 20:00:00+0000 |  152.5 | 
| September, 30 2012 20:00:00+0000 |  52.5 | 
| October, 08 2012 20:00:00+0000 |  41 | 
| October, 14 2012 20:00:00+0000 |  6037 | 
| October, 15 2012 20:00:00+0000 |  6610 | 
| October, 16 2012 20:00:00+0000 | 5624.6667 | 
| October, 21 2012 20:00:00+0000 | 1649.6667 | 
| October, 28 2012 20:00:00+0000 |  31 | 
| November, 04 2012 19:00:00+0000 |  10 | 
+0

在'INTERVAL'中引用'-6'後,將MySQL引用改爲ANSI引號或刪除引號,將DATE_ADD改爲使用'+'運算符並添加一個' ORDER BY',這個查詢也適用於PostgreSQL(請參閱:http://sqlfiddle.com/#!12/275eb/8)...但我會在那裏使用窗口函數解決方案。 –

+0

我真的很想看到一個窗口函數的解決方案。 –

+0

@EvanZamir我沒有時間查看它,而不是Postgres用戶。但應該很容易找到例子。上述解決方案的一個優點是它可以跨平臺移植(如Craig所示),只是日期函數不同。 – RedFilter

4

RedFilter的優秀answer使用非均勻間隔的桶。如果你想均勻分佈的水桶做這樣的事情:

SELECT 
DATE_FORMAT(CURDATE() - INTERVAL ((i.id - 0) * 7) - 1 DAY, '%Y-%m-%d') AS start, 
DATE_FORMAT(CURDATE() - INTERVAL ((i.id - 1) * 7) DAY, '%Y-%m-%d') AS end, 
AVG(d.value) average 
FROM 
ids i 
LEFT OUTER JOIN 
dates d ON 
d.date BETWEEN 
    (CURDATE() - INTERVAL ((i.id - 0) * 7) - 1 DAY) AND 
    (CURDATE() - INTERVAL ((i.id - 1) * 7) DAY) 
WHERE 
i.id BETWEEN 0 AND 20 
GROUP BY 
1 
ORDER BY 
1 DESC; 

提供這樣的:

+------------+------------+-----------+ 
| start  | end  | average | 
+------------+------------+-----------+ 
| 2012-11-01 | 2012-11-07 | 10.0000 | 
| 2012-10-25 | 2012-10-31 | 31.0000 | 
| 2012-10-18 | 2012-10-24 | 108.0000 | 
| 2012-10-11 | 2012-10-17 | 5624.6667 | 
| 2012-10-04 | 2012-10-10 | 41.0000 | 
| 2012-09-27 | 2012-10-03 | 85.0000 | 
| 2012-09-20 | 2012-09-26 | 152.5000 | 
| 2012-09-13 | 2012-09-19 | 20.0000 | 
| 2012-09-06 | 2012-09-12 | 20.0000 | 
| 2012-08-30 | 2012-09-05 | 41.0000 | 
| 2012-08-23 | 2012-08-29 | 63.0000 | 
| 2012-08-16 | 2012-08-22 | 52.0000 | 
| 2012-08-09 | 2012-08-15 | 160.0000 | 
| 2012-08-02 | 2012-08-08 |  NULL | 
| 2012-07-26 | 2012-08-01 |  NULL | 
| 2012-07-19 | 2012-07-25 |  NULL | 
| 2012-07-12 | 2012-07-18 |  NULL | 
| 2012-07-05 | 2012-07-11 |  NULL | 
| 2012-06-28 | 2012-07-04 |  NULL | 
| 2012-06-21 | 2012-06-27 |  NULL | 
+------------+------------+-----------+ 
20 rows in set (0.00 sec) 

http://sqlfiddle.com/#!2/78c52/39的工作示例不CURDATE()。

+0

因爲花了我一點時間才弄清楚:ids表用於幫助數學來定義範圍並提供「限制」功能。在SQL Fiddle中進行測試表明,此方法比使用「limit 20」有更好的查詢計劃。 – cogg

+0

小問題:「BETWEEN 1 AND 20」代表更好的使用的值,「GROUP BY 1,2」不會取決於MySQL的能力省略分組欄。 – cogg