2017-05-23 91 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 –