2017-05-23 68 views
0

我有一个查询(带有子查询),可以计算前几年的平均温度,每天加/减一周。它有效,但并不是那么快。下面的时间序列值只是一个例子。为什么我使用doy是因为我想在每年的同一日期附近有一个滑动窗口。PostgreSQL优化:日期范围内的平均值

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 

所以我的问题是,这个查询可以以某种方式改进?我正在考虑使用某种窗口函数,或者可能使用laglead。但是,至少常规的窗口函数仅适用于特定数量的行,而在两周窗口内可以进行任意数量的测量。

我可以和我现在所拥有的一样,但随着数据量的增长,查询的执行速度也如此。后两个extract可以被删除,但是没有明显的速度改进,只会使查询不易读。任何帮助将不胜感激。

+0

搜索“优化搜索”,我建议提供现有查询的解释计划术语。 –

回答

1

您原始查询的最佳指标是

create index idx_temperatures_site_id_timestamp_doy 
    on temperatures(site_id, extract(doy from timestamp)); 

这可以大大提高您的原始查询的性能。

尽管您的原始查询很简单,但它有一个缺陷:它会计算每天平均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开头的索引。无论如何,使用窗口版本应该会提高性能。

http://rextester.com/XTJSM42954

+0

一个有趣的方法,肯定比我的原始方法快得多。我最初的尝试确实是在“doy”上索引表,但这不起作用,因为显然'extract doy'不是不可变的。无论如何,这对我所拥有的数据来说要快得多。 –

+0

@TeemuKarimerto这是因为你的专栏实际上是'timestamptz'。请参阅我的编辑(**注**)。 – pozs

+0

啊,是的,这似乎是与索引问题。我宁愿使用'timestamp',但这些都是Django生成的表格,我不完全确定我应该如何去转换数据库中的值并配置Django,因此没有任何问题:D –