2015-04-23 54 views
4

我運行的Postgres 9.2和我有一個大的表像滾動平均的Postgres

CREATE TABLE sensor_values 
(
    ts timestamp with time zone NOT NULL, 
    value double precision NOT NULL DEFAULT 'NaN'::real, 
    sensor_id integer NOT NULL 
) 

我進入系統的價值不斷地即多每分鐘。我想維持最後200個值的滾動標準偏差/平均值,因此我可以確定進入系統的新值是否在平均值的3個標準偏差之內。要做到這一點,我需要當前的標準偏差和意思是不斷更新最後200個值。 由於表格可能有數億行,我不想最後說200行的傳感器按時間排序,然後對每個新值進行vg(value),var_samp(value)。我假設更新標準偏差和平均值會更快。

我已經開始編寫一個PL/pgSQL函數來更新每個進入特定傳感器系統的新值的滾動方差和平均值。

我可以做到這一點使用碼僞像

newavg = oldavg + (new_value - old_value)/window_size 
new_variance += (new_value-old_value)*(new_value-newavg+old_value-oldavg)/(window_size-1) 

這是基於 http://jonisalonen.com/2014/efficient-and-accurate-rolling-standard-deviation/

基本上窗口大小200個的值。 old_value是窗口的第一個值。當有新的價值出現時,我們將窗口向前移動一個。之後,我得到的結果我存儲以下值傳感器

The first value of the window. 
The mean average of the window values. 
The variance of the window values. 

這樣我就不必不斷那裏持續200價值,做一個總和etc.I可以重複使用這個值時,一個新的傳感器值進來

我的問題是,當第一次運行我沒有一個傳感器的前一個窗口數據,即上述三個值,所以我必須做的慢的方式。

WITH s AS 
     (SELECT value FROM sensor_values WHERE sensor_values.sensor_id = $1 AND ts >= (NOW() - INTERVAL '2 day')::timestamptz ORDER BY ts DESC LIMIT 200) 
    SELECT avg(value), var_samp(value) INTO last_window_average, last_window_variance FROM s; 

但我怎麼能得到最後的值(ealiest),以從select語句救? 我可以在PL/pgSQL中訪問s中的第一行嗎?

我認爲PL/pgSQL會更快/更乾淨的方法,但也許它更好地做到這一點是客戶端代碼? 有沒有更好的方法在滾動統計更新上執行此類型?

+1

什麼'AVG(值)超過(前述200之間分區由sensor_id爲了通過TS行和當前行)作爲avg' –

回答

0

我認爲,每次通過適當的索引重新計算最近的200個條目的速度並不會很慢。如果你會做一個索引,如:

CREATE INDEX i_sensor_values ON sensor_values(sensor_id, ts DESC); 

你就可以快速得到結果還算做:

SELECT sum("value") -- add more expressions as required 
    FROM sensor_values 
WHERE sensor_id=$1 
ORDER BY ts DESC 
LIMIT 200; 

您可以在一個循環從PL/pgSQL函數執行這個查詢。 如果您很快就會遷移到9.3(或更高版本),那麼您也可以使用LATERAL joins來實現此目的。

我不認爲一個覆蓋索引會做一件好事,在這裏,因爲表是不斷變化的,IndexOnlyScan不會踢。

這是好事,檢查Loose Index scans也。

P.S.列名value應該雙引號,因爲這是一個SQL reserved word

+0

嗯做 SELECT sensor_id,值 FROM sensor_values WHERE sensor_id = 555 ORDER BY TS DESC LIMIT 200; 花了兩分鐘,因爲它必須訂購所有的數據。 1秒緩存。 http://explain.depesz.com/s/DbN 做 SELECT ts,value FROM sensor_values WHERE sensor_values.sensor_id = 540 AND ts> =(NOW() - INTERVAL'2 day'):: timestamptz ORDER BY ts DESC LIMIT 200需要100 ms 。 當我一分鐘可以獲得一千個新條目時,我認爲這是太多的開銷。 我認爲索引工作正常。 也許我可以使它不是每1000個左右的實時條目。 –

+0

@GlennPierce,你還沒有提到分區。當然,要獲得適當的分區修剪,您需要添加適當的謂詞。你能說出第二個查詢的計劃嗎? – vyegorov

+0

第二個查詢的計劃 http://explain.depesz.com/s/tXrD 不知道爲什麼它必須檢查早些年的分區表 –