我正在运行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会更快/更清洁,但也许更好的做法是客户端代码? 滚动统计更新是否有更好的方法来执行此类型?
答案 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。