私はいくつかの時系列データで単純な選択を区別しています:
SELECT DISTINCT user_id
FROM events
WHERE project_id = 6
AND time > '2015-01-11 8:00:00'
AND time < '2015-02-10 8:00:00';
そしてそれは112秒かかります。クエリプランは次のとおりです。
http://explain.depesz.com/s/NTyA
私のアプリケーションは、多くの異なる操作を実行する必要があり、このようにカウントします。この種のデータを取得するより速い方法はありますか?
あなたはおそらくこれを聞きたくないでしょうが、SELECT DISTINCT
を高速化する最良のオプションはavoidDISTINCT
toで始まる。多くの場合(すべてではありません!)、より良いデータベース設計またはより良いクエリで回避できます。
場合によっては、GROUP BY
の方が高速です。これは、コードパスが異なるためです。
あなたの特定のケースでは、DISTINCT
を取り除くことができないようです。ただし、そのようなクエリが多数ある場合は、専用のインデックスを使用してクエリをサポートできます。
CREATE INDEX foo ON events (project_id, "time", user_id);
user_id
の追加は、これから index-only scans を取得した場合にのみ役立ちます。詳細はリンク先をご覧ください。高価なを削除します ビットマップヒープスキャン クエリプランから。クエリ時間の90%を消費します。
EXPLAIN
の出力から、クエリでは50万件の一致する行から2,491人の異なるユーザーを要約する必要があることがわかります。これは、あなたが何をしても、超高速になることはありませんが、大幅に高速になる可能性があります。
クエリの時間間隔が常に同じである場合、 MATERIALIIZED VIEW
フォールディングuser_id
per (project_id, <fixed time intervall>)
は大いに役立ちます。ただし、時間間隔が変化する可能性はありません。おそらく、少なくとも1時間あたりのユーザー数またはその他の最小時間単位をフォールドでき、それによってかなりのオーバーヘッドを保証するのに十分なパフォーマンスが得られます。
Nitpick:
おそらく、"time"
の述語は実際には次のようになります。
AND "time" >= '2015-01-11 8:00:00'
AND "time" < '2015-02-10 8:00:00';
余談:
識別子としてtime
を使用しないでください。これは、標準SQLでは 予約語 であり、Postgresでは基本型です。
これがサムのケースでの私のテストとアーウィンの答えです
drop table t1
create table t1 (id int, user_id int, project_id int, date_time timestamp without time zone) ;
insert into t1 -- 10 million row - size="498 MB"
select row_number() over(), round(row_number() over()/1000), round(row_number() over()/100000) , date
from generate_series('2015-01-01'::date, '2016-12-01'::date,'6 seconds'::interval
) date
limit 10000000
-- before indexing - 10000000 row - output=100 row - time=2900ms
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 8:00:00'
AND date_time < '2016-12-01 8:00:00' ;
CREATE INDEX foo ON t1 (project_id, date_time, user_id); -- time process=51.2 secs -- size="387 MB"
-- after indexing - 10000000 row - output=100 row - time= 75ms (reduce ~ 38 times)
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 00:00:00'
AND date_time < '2016-12-01 00:00:00' ;
アーウィン氏は、「これは聞きたくないかもしれませんが、SELECT DISTINCTを高速化するための最良のオプションは、DISTINCTを最初から回避することです。多くの場合(すべてではありません!)、より良いデータベース設計またはより良いクエリで回避できます。 」私は彼が正しいと思います、「もしあれば」「個別、グループ化、順序付け」の使用を避けるべきです。
サムのケースとして状況に遭遇し、サムは月ごとにイベントテーブルのパーティションを使用できると思います。クエリを実行するとデータサイズが減少しますが、上記のクエリの代わりに実行する関数(pl/pgsql)が必要です。関数は、queryを実行するための適切なパーティションを(条件に応じて)見つけます。