平均帖子

时间:2015-04-23 09:19:18

标签: sql postgresql plpgsql window-functions

我正在运行Postgres 9.2,我有一个类似

的大表
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(值),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个值并做一笔总和等。当新的传感器值进入时,我可以重复使用这个值。

我的问题是,第一次运行时我没有传感器的先前窗口数据,即上面的三个值,所以我必须以缓慢的方式进行。

类似

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;

但是我怎样才能从select语句中获取最后一个值(最早)? 我可以从PL / pgSQL中的s访问第一行。

我认为PL / pgSQL会更快/更清洁,但也许更好的做法是客户端代码? 滚动统计更新是否有更好的方法来执行此类型?

1 个答案:

答案 0 :(得分: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