2015-04-23 63 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 不知道为什么它必须检查早些年的分区表 –