我想直接从使用TimescaleDB存储在PostgreSQL-DB中的数据中删除峰值。
我的数据存储为间隔1秒的值,我希望获得5分钟的平均值,而不会出现峰值。
我使用标准偏差确定尖峰,并排除距离固定zscore多的所有数据。
因此,第一步是获取与分析相关的所有数据(数据过滤后),然后计算每个5分钟数据块的平均值和标准差(avg_and_stddev_per_interval),然后将初始数据(数据过滤后)与计算出的平均值和stddev,排除所有不符合我的条件的值,并最终计算出最终的5分钟平均值而不出现峰值。
with data_filtered as (
select ts, value
from schema.table
where some_criteria = 42
and ts >= '2018-11-12 10:00:00'
and ts < '2018-11-13 10:00:00'
),
avg_and_stddev_per_interval as (
select time_bucket('5 minutes', ts) as five_min,
avg(value) as avg_value,
stddev(value) as stddev_value,
from data_filtered
group by five_min
)
select
time_bucket('5 minutes', ts) as tb,
avg(value) as value,
from data_filtered
left join avg_and_stddev_per_interval
on data_filtered.ts >= avg_and_stddev_per_interval.five_min
and data_filtered.ts < avg_and_stddev_per_interval.five_min + interval '5 minutes'
where abs((value-avg_value)/stddev_value) < 1
group by tb;
一切正常,但是速度非常慢。请求没有任何分组(select * from data_filtered
)的完整数据并在本地计算我的条件要快得多。但是,我想减少数据量,所以这种方法在这种情况下是不可能的。
有什么方法可以加快查询速度吗?
答案 0 :(得分:0)
eurotrash'注释导致更快的代码如下:
COALESCE()
在这里,我摆脱了仅出于可读性考虑而存在的所有CTE。
尽管如此,这仍然比不要求去除尖峰的平均值要慢8倍。
解释分析:
select
time_bucket('5 minutes', ts) as tb, avg(value) as value
from schema.table
left join (
select time_bucket('5 minutes', ts) as five_min,
avg(value) as value,
stddev(value) as stddev_value,
from schema.table
where some_criteria = 42
and ts >= '2018-11-12 00:00:00'
and ts < '2018-11-13 00:00:00'
group by five_min
) as fm
on ts >= fm.five_min
and ts < fm.five_min + interval '5 minutes'
where some_criteria = 42
and ts >= '2018-11-12 00:00:00'
and ts < '2018-11-13 00:00:00'
and abs((value-avg_value)/stddev_value) < 1
group by tb;
答案 1 :(得分:0)
最简单的方法是用(临时)视图替换CTE零件。这将使优化程序可以改组并重新组装查询部分。
CREATE TEMP VIEW data_filtered as
SELECT ts, value
FROM schema.table
WHERE some_criteria = 42
AND ts >= '2018-11-12 10:00:00'
AND ts < '2018-11-13 10:00:00'
;
CREATE TEMP VIEW avg_and_stddev_per_interval as
SELECT time_bucket('5 minutes', ts) as five_min
, avg(value) as avg_value
, stddev(value) as stddev_value
FROM data_filtered
GROUP BY 1
;
SELECT
time_bucket('5 minutes', ts) as tb
, avg(value) as value
FROM data_filtered df
LEFT JOIN avg_and_stddev_per_interval av
ON df.ts >= av.five_min
AND df.ts < av.five_min + interval '5 minutes'
WHERE abs((value-avg_value)/stddev_value) < 1
GROUP BY 1
;
答案 2 :(得分:0)
似乎最糟糕的性能发生在JOIN中(根据答案中的查询而不是问题中的查询)。理想情况下,当子查询返回很多结果时,您就不会加入该子查询,但是根据您的条件,我看不出如何避免它。
这是我的建议:
现在我通常讨厌这样做,因为我不喜欢创建临时表,但是有时候它确实确实为您提供了其他方法无法实现的最佳性能。 (不是说不能用另一种方法来做,但是我想不出一种更好的性能方法。)
是这样的:
CREATE OR REPLACE FUNCTION schema.my_function()
RETURNS TABLE (tb SOMETYPE, avg NUMERIC) AS
$BODY$
BEGIN
CREATE TEMP TABLE fm ON COMMIT DROP AS
select time_bucket('5 minutes', ts) as five_min,
avg(value) as value,
stddev(value) as stddev_value
from schema.table
where some_criteria = 42
and ts >= '2018-11-12 00:00:00'
and ts < '2018-11-13 00:00:00'
group by five_min;
CREATE INDEX ON fm (five_min);
RETURN time_bucket('5 minutes', ts), avg(value)
from schema.table
left join fm
on ts >= fm.five_min
and ts < fm.five_min + interval '5 minutes'
where some_criteria = 42
and ts >= '2018-11-12 00:00:00'
and ts < '2018-11-13 00:00:00'
and abs((value-avg_value)/stddev_value) < 1
group by tb;
END
$BODY$
LANGUAGE plpgsql;
很明显,我创建的索引只是基于您发布的查询中的示例,尽管我看到实际的查询包含其他内容,所以您希望对要连接的任何字段建立索引。
我称tb的类型为SOMETYPE,因为我不知道time_bucket返回什么类型。当然,您可以传递查询中应该可变为参数的任何部分。