sql - 滚动平均postgres

标签 sql postgresql plpgsql window-functions

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

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 个值并进行求和等操作。当有新的传感器值出现时,我可以重复使用这些值。

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

有点像

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 会是更快/更清洁的方法,但也许更好的做法是使用客户端代码? 有没有更好的方法来执行这种类型的滚动统计更新?

最佳答案

我假设,每次使用适当的索引重新计算最新的 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还有。

附言列名 value 应该用双引号引起来,因为这是一个 SQL reserved word .

关于sql - 滚动平均postgres,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29819010/

相关文章:

sql - 将管理员用户与前端用户放在同一个表中是好的数据库设计吗?

SQL - 'THEN' 语句中的多个 'CASE WHEN' 选项

sql - 在子查询中使用 LIMIT 并在子查询和外部查询中重复 WHERE 子句来简化查询

sql - 重构 PostgreSQL 查询以返回百分比而不是计数

sql - 将标签数组传递给 plpgsql 函数并在 WHERE 条件下使用它

postgresql - 一个 Postgresql 函数卡在 for 循环中,查询永远不会结束查询

sql - 使用 RANK OVER PARTITION 比较前一行结果

java - 使用 glassfish server 4.1.1 自动部署 web 服务 java?

sql - 将多个值集或数组传递给一个函数

php - 计算年龄: PHP vs MySQL,哪种方法更好?