2017-05-23 13 views
0

私は、前年度の平均気温をプラス/マイナス1日ごとに計算するクエリ(サブクエリ付き)を持っています。それは動作しますが、それほど高速ではありません。以下の時系列値は一例に過ぎません。私がなぜ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を考えていた。しかし、少なくとも正規のウィンドウ関数は特定の行量でしか動作しませんが、2週間のウィンドウ内では任意の数の測定が可能です。

私は今のところ生きていますが、データ量が増えるほどクエリの実行速度も向上します。後者の2つのextractは削除することができますが、速度の向上は目立たず、クエリの読みやすさが低下します。どんな助けでも大歓迎です。

+0

用語を検索する「検索引数可能」と私は、既存のクエリの実行計画を提供することをお勧めします。 –

答えて

1

あなたの元のクエリのための最良の指標は大幅にあなたの元のクエリのパフォーマンスを向上させることができ

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

これです。

元のクエリはシンプルで&ですが、1つの欠陥があります:毎日の平均を14回(平均)計算します。代わりに、これらの平均を毎日計算することができます。& 2週間のウィンドウの加重平均を計算します(1日の平均の重みは元のテーブルの個々の行の数である必要があります)。このような何か:これが唯一の倍の速度(最適なインデックス付き)あなたの元のクエリとしてあるよう

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)を使用します。このタイプでは、that expression's output is dependent of the actual client's time zone settingのようにextract(doy from timestamp)式にインデックスを付けることはできません。

timestamptzについては、少なくともsite_idで始まるインデックスを使用してください。とにかくウィンドウバージョンを使用すると、パフォーマンスが向上します。

http://rextester.com/XTJSM42954

+0

興味深いアプローチで、確かに私のオリジナルよりもはるかに高速です。私の最初の試みは実際に 'doy'のテーブルを索引付けしていましたが、明らかに' extract doy'は不変ではないので動作しません。いずれにせよ、これは私が持っているデータではるかに高速です。 –

+0

@TeemuKarimertoこれはあなたの列が実際には 'timestamptz'だからです。私の編集内容を見てください(**注**)。 – pozs

+0

ああ、それは索引付けの問題であるようです。 'timestamp'を使うのが好きですが、これはDjangoで生成されたすべてのテーブルで、データベースの値を変換してDjangoを設定することはできません。D –