PostgreSQL 优化:日期范围内的平均值

PostgreSQL optimization: average over range of dates

我有一个查询(带有子查询)计算过去几年的平均温度,plus/minus 每天一个星期。它有效,但并不是那么快。下面的时间序列值只是一个例子。为什么我使用 doy 是因为我想在每年的同一日期前后滑动 window。

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

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

我可以接受目前拥有的东西,但随着数据量的增长,查询的执行速度也会增加。后两个 extract 可以删除,但这没有明显的速度提升,只会使查询更难辨认。任何帮助将不胜感激。

原始查询的最佳索引是

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

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

虽然您的原始查询简单易读,但它有 1 个缺陷:它会计算每天的平均值 14 次(平均)。相反,您可以每天计算这些平均值并计算 2 周 window 的加权平均值(一天平均值的权重需要计算原始 table 中的各个行)。像这样:

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 开头的索引。使用 window 版本应该会提高性能。

http://rextester.com/XTJSM42954