我有一个查询(带有子查询),用于计算前几年的平均温度,每天加/减一周。它有效,但速度并不快。下面的时间序列值只是一个例子。为什么我使用doy
是因为我想在每年的同一天左右推出一个滑动窗口。
SELECT days,
(SELECT avg(temperature)
FROM temperatures
WHERE site_id = ? AND
extract(doy FROM timestamp) BETWEEN
extract(doy FROM days) - 7 AND extract(doy FROM days) + 7
) AS temperature
FROM generate_series('2017-05-01'::date, '2017-08-31'::date, interval '1 day') days
所以我的问题是,这个查询能以某种方式得到改善吗?我正在考虑使用某种窗口函数,或者可能lag
和lead
。但是,至少常规窗口函数仅适用于特定数量的行,而在两周窗口内可以有任意数量的测量。
我现在可以使用我现有的东西,但随着数据量的增长,查询的执行速度也会增加。可以删除后两个extract
,但这没有明显的速度提升,只会使查询不太清晰。任何帮助将不胜感激。
答案 0 :(得分:1)
原始查询的最佳索引是
create index idx_temperatures_site_id_timestamp_doy
on temperatures(site_id, extract(doy from timestamp));
这可以大大提高原始查询的效果。
虽然您的原始查询很简单&可读,它有1个缺陷:它会计算每天平均14次(平均)。相反,您可以每天计算这些平均值&计算2周窗口的加权平均值(一天平均值的权重需要是原始表中各行的计数)。像这样:
with p as (
select timestamp '2017-05-01' min,
timestamp '2017-08-31' max
)
select t.*
from p
cross join (select days, sum(sum(temperature)) over pn1week / sum(count(temperature)) over pn1week
from p
cross join generate_series(min - interval '1 week', max + interval '1 week', interval '1 day') days
left join temperatures on site_id = ? and extract(doy from timestamp) = extract(doy from days)
group by days
window pn1week as (order by days rows between 7 preceding and 7 following)) t
where days between min and max
order by days
然而,这里获得的收益并不多,因为这只是原始查询的两倍(具有最佳索引)。
http://rextester.com/JCAG41071
备注:我使用timestamp
,因为我认为您的列类型为timestamp
。但事实证明,您使用timestamptz
(又名。timestamp with time zone
)。使用该类型,您无法为extract(doy from timestamp)
表达式编制索引,因为that expression's output is dependent of the actual client's time zone setting。
对于timestamptz
,请使用(至少)以site_id
开头的索引。使用窗口版本无论如何都应该提高性能。