2016-08-05 5 views
9

私は実行しようとしているPostgresクエリに問題があります - 私は問題をフレーミングする方法をたくさん試しましたが、これまでの喜びはありませんでした。Postgresサブクエリは、参加時に非常にゆっくり実行されています

私はうまくいくいくつかのクエリを書くことができましたが、重要なのはパフォーマンスの1つです。動作するクエリは使いすぎるほどです。

私はevents_hubと呼ばれるテーブルを持ち、異なるイベントに関する情報を含む別々のテーブルにリンクしています。異なるイベントは異なるevent_typesによって区別されます。これらのイベントは集約にグループ化され、集約はaggregate_idによって区別されます。

私の基本的な問題は、各集約グループのイベント1に関連付けられた最も早い時刻を見つけて、その時刻までの時間枠内でイベント2の発生数をカウントすることです。時間2のイベントは、集約グループが最初に出現する前の24時間に発生します)。

イベントハブテーブルには、次のようなものになります。aggregate_id 1の初期の発生は3を持っているので

| aggregate_id | count_of_event2 | 
---------------------------------- 
|  1  |  3  | 
|  2  |  2  | 
---------------------------------- 

:上記の玩具の例では

| aggregate_id | event_id | event_type | event_time | 
------------------------------------------------------- 
|  1  |  1 |  1  | 1st Jan | 
|  1  |  2 |  1  | 2nd Jan | 
|  2  |  3 |  1  | 2nd Jan | 
|  2  |  4 |  1  | 3rd Jan | 
|  null |  5 |  2  | 30th Dec | 
|  null |  6 |  2  | 31st Dec | 
|  null |  7 |  2  | 1st Jan | 
|  null |  8 |  2  | 1st Jan | 
------------------------------------------------------- 

を、私は返すようにしたいと思いますその前日にevent_type 2が発生し、aggregate_id 2には2回しか発生しません。

アプローチ1

私の最初の試みはして、グループに囲まれた結合を使用することを含みます。次のクエリは非常に素早く実行されますが、私が欲しいものを正確に返さない:この上EXPLAIN ANALYZEを実行

SELECT 
    aggregate_id, 
    count(aggregate_id) 
FROM 
    (SELECT 
     aggregate_id, 
     min(event_time) as time_of_event1 
    FROM events_hub WHERE event_type = 1 
    GROUP BY aggregate_id) as t1 
    LEFT JOIN 
    (SELECT event_time as time_of_event2 
    FROM events_hub WHERE event_type = 2) as t2 
    ON t2.time_of_event2 BETWEEN t1.time_of_event1 - INTERVAL '24 hours' 
          AND t1.time_of_event1 
GROUP BY aggregate_id 

は、次の(この問題のSQLクエリは、私がしたい、実際のクエリのバージョンが減少していることに注意してください返します実行したい - ので、実行計画に表示されたテーブルには、いくつかの余分な制限があります):

HashAggregate (cost=1262545.21..1262547.21 rows=200 width=15) (actual time=536.206..539.222 rows=2824 loops=1) 
    Group Key: events_hub_1.aggregate_id 
    -> Nested Loop Left Join (cost=9137.36..1191912.59 rows=14126523 width=15) (actual time=15.419..395.895 rows=111948 loops=1) 
     -> HashAggregate (cost=9136.80..9141.42 rows=462 width=23) (actual time=15.387..19.316 rows=2824 loops=1) 
       Group Key: events_hub_1.aggregate_id 
       -> Index Only Scan using comp_index1 on events_hub events_hub_1 (cost=0.56..9110.87 rows=5186 width=23) (actual time=2.669..9.750 rows=4412 loops=1) 
        Index Cond: ((event_type_code = 5) AND (event_datetime >= '2013-01-01 00:00:00'::timestamp without time zone) AND (event_datetime <= '2013-01-02 00:00:00'::timestamp without time zone) AND (aggregate_id IS NOT NULL)) 
        Heap Fetches: 4412 
     -> Index Only Scan using comp_index on events_hub (cost=0.56..2254.33 rows=30577 width=8) (actual time=0.005..0.049 rows=40 loops=2824) 
       Index Cond: ((event_type_code = 3) AND (event_datetime <= (min(events_hub_1.event_datetime))) AND (event_datetime >= ((min(events_hub_1.event_datetime)) - '12:00:00'::interval))) 
       Heap Fetches: 0 
Planning time: 0.326 ms 
Execution time: 542.020 ms 

これは私がイベントのハブに複合インデックス(event_type, event_time)を持っているとして、特に驚くべきことではないので、比較的2つのイベント実行の相対時間に基づく複雑な結合条件早く。

しかし、イベント2の属性のいくつかに基づいてクエリに別の条件を追加しようとすると、クエリが大幅に遅くなります(上記のクエリはこのクエリの

SELECT 
    aggregate_id, 
    count(aggregate_id) 
FROM 
    (SELECT 
     aggregate_id, 
     min(event_time) as time_of_event1 
    FROM events_hub WHERE event_type = 1 
    GROUP BY aggregate_id) as t1 
    LEFT JOIN 
    (SELECT event_id, event_time as time_of_event2 
    FROM events_hub WHERE event_type = 2) as t2 
    ON t2.time_of_event2 BETWEEN t1.time_of_event1 - INTERVAL '24 hours' 
          AND t1.time_of_event1 
    INNER JOIN 
    (SELECT event_id FROM event_2_attributes WHERE some_flag = TRUE) as t3 
    ON t2.event_id = t3.event_id 
GROUP BY aggregate_id 

EXPLAIN ANALYZEクエリが返す:分以下意志の実行)に対し、フラッシュ、

HashAggregate (cost=33781.17..33783.17 rows=200 width=15) (actual time=479888.736..479891.819 rows=2824 loops=1) 
    Group Key: events_hub_1.aggregate_id 
    -> Nested Loop (cost=9625.94..33502.10 rows=55815 width=15) (actual time=346721.414..479857.494 rows=26164 loops=1) 
     Join Filter: ((events_hub.event_datetime <= (min(events_hub_1.event_datetime))) AND (events_hub.event_datetime >= ((min(events_hub_1.event_datetime)) - '12:00:00'::interval))) 
     Rows Removed by Join Filter: 209062796 
     -> Merge Join (cost=489.14..14311.03 rows=1087 width=8) (actual time=1.360..1571.387 rows=74040 loops=1) 
       Merge Cond: (events_hub.event_id = arrests.event_id) 
       -> Index Scan using comp_index4 on events_hub (cost=0.44..290158.71 rows=275192 width=12) (actual time=1.344..512.787 rows=282766 loops=1) 
        Index Cond: (event_type_code = 3) 
       -> Index Scan using arrests_events_id_index on arrests (cost=0.42..11186.59 rows=73799 width=4) (actual time=0.008..456.550 rows=74040 loops=1) 
        Filter: felony_flag 
        Rows Removed by Filter: 210238 
     -> Materialize (cost=9136.80..9148.35 rows=462 width=23) (actual time=0.001..3.002 rows=2824 loops=74040) 
       -> HashAggregate (cost=9136.80..9141.42 rows=462 width=23) (actual time=10.963..14.006 rows=2824 loops=1) 
        Group Key: events_hub_1.aggregate_id 
        -> Index Only Scan using comp_index1 on events_hub events_hub_1 (cost=0.56..9110.87 rows=5186 width=23) (actual time=0.018..5.405 rows=4412 loops=1) 
          Index Cond: ((event_type_code = 5) AND (event_datetime >= '2013-01-01 00:00:00'::timestamp without time zone) AND (event_datetime <= '2013-01-02 00:00:00'::timestamp without time zone) AND (aggregate_id IS NOT NULL)) 
          Heap Fetches: 4412 
Planning time: 12.548 ms 
Execution time: 479894.888 ms 

が含まれている場合、内部結合、少ないデータが実際にあることに注意してくださいb eingが返されました。それでもまだそれはずっと遅いです。

これらの結合を入れ子にして、LEFT JOINではなくRIGHT JOINになるように切り詰めていますが、違いはありません。

また、各サブクエリのCTE式を試して実行順序を強制しようとしましたが、そこには運がありません。これはかなりうまく機能し、約15秒で走る

SELECT 
    t1.aggregate_id, 
    (SELECT count(t3.event_id) 
    FROM (SELECT event_id FROM events_hub AS t2 WHERE t2.event_type = 2 
      AND t2.event_time BETWEEN t1.time_of_event1 - INTERVAL '24 hours' 
          AND t1.time_of_event1) as t3 
      INNER JOIN event_2_attributes as t4 
      ON t3.event_id = t4.event_id 
      WHERE t4.some_flag = TRUE) as count_column 
FROM 
    (SELECT 
     aggregate_id, 
     min(event_time) as time_of_event1 
    FROM events_hub WHERE event_type = 1 
    GROUP BY aggregate_id) as t1 

第二のアプローチとして、アプローチ2

、私はイベント2の数を返すサブクエリを使用してみてください。しかし、私は試してみて結果を取得し、(私が次に何を何のために必要とされる)別のテーブルに挿入したときに、クエリが実行するのに膨大な時間を要する:

CREATE TABLE tbl AS 
    < query above > 

これは私には不可解です!

私はこのクエリでEXPLAIN ANALYZEを実行しようとしましたが、終了する前に2000秒になっています。上記のように、EXPLAIN ANALYZEがないと、これは15秒で実行されます。最終的アプローチとして

アプローチ3

、私は横を使用して試した、以下のように(ここでグループなし)参加:

WITH t1 AS 
(SELECT 
    aggregate_id, 
    min(event_time) as time_of_event1 
FROM events_hub WHERE event_type = 1 
GROUP BY aggregate_id) 
SELECT 
    t1.aggregate_id, 
    t2.event_time 
FROM t1 
LEFT JOIN LATERAL 
    (SELECT event_time FROM 
     (SELECT event_id, event_time FROM events_hub WHERE event_type = 2) as t3 
     INNER JOIN 
     (SELECT event_id FROM event_2_attributes WHERE some_flag = TRUE) as t4 
     ON t3.event_id = t4.event_id 
    WHERE t3.event_time BETWEEN t1.time_of_event1 - INTERVAL '24 hours' 
         AND t1.time_of_event1 
    ) as t2 
ON TRUE 

このクエリは非常に、再び実行されますが、 、非常にゆっくり - 操作してもグループがなくても。


これらのもの(おそらく無関係なもの)に光を当てると、大いに感謝します。イベントハブの個々の列にはインデックスが付けられていると言えるでしょう。

多くの感謝!

+0

パフォーマンスに関する質問には、「EXPLAIN ANALYZE」とテーブルサイズ、インデックス、現在の時間パフォーマンス、欲求時間などの情報が含まれている必要があります。「遅い」は相対的な用語であり、比較するには実際の値が必要です。 –

+0

あなたの欲望の結果を返すクエリは何ですか?もしaproach1がすでに素早くなっているのなら、なぜ2と3が必要なのでしょうか? –

+0

現時点での作業[sqlFiddle](http://sqlfiddle.com/#!15/2fe24/1)を試してみると、サンプルデータに適切なインデックスを含む 'event_type_code'がありません。 –

答えて

1

OKのように書き換えてみてください、私はこれを考え出しました。追加するために、次に

CREATE TABLE earliest_time AS 
(SELECT 
    aggregate_id, 
    min(event_time) as time_of_event1 
FROM events_hub WHERE event_type = 1 
GROUP BY aggregate_id) 

そして:

'はneatestのソリューションのではないが、最終的なトリックをaggregate_idに関連付けられた最も早い時刻を返す初期GROUP BY操作の結果を含むテーブルを作成するましたaggregate_idtime_of_event1の両方の列にインデックスを付けます。

この表は、上記アプローチ1に従って使用した。

サブクエリがすでにマテリアライズされていると、プランナは最も効率的なパスを選択するのに役立ち、実行時間は2桁減少します。

0

EXPLAIN ANALIZEが含まれていないために役立つかどうかわかりませんが、サブクエリを作成して参加すると、通常はインデックスの使用が失われます。

この

SELECT e.event_id, e.event_time, ea.event_id -- but dont think you need it repeat event_id 
FROM events e 
INNER JOIN event_2_attributes ea 
     ON e.event_id = ea.event_id 
WHERE e.event_type = 2 
    AND ea.some_flag = TRUE 
+0

LEFT JOIN(SELECT ... FROM xyz WHERE [condition])はLEFT JOIN xyz ON [condition] 'と同じです。 – oals

関連する問題