2016-04-26 4 views
2

私は2つのテーブル:campaignstatsを持っています。 statsテーブルには、すべてのキャンペーンについて集計しようとしている毎日の統計情報が含まれています。なぜPostgresは条件付き結合の私のすべてのインデックスを無視していますか?

私は考えることができるすべてのフィールドを索引付けしましたが、私が知ることのできる索引はどれも使用されていません。私はPostgresがインデックスを使用しないことを選択するかもしれないが、それは疑わしいと思われ、クエリも稲妻ではありません。どのように私はそれを助けることができる?

EXPLAIN ANALYZE SELECT "campaign"."id", "campaign"."name", "campaign"."status", SUM("stats"."impressions") AS "impressions" 
    FROM "campaign" 
    LEFT OUTER JOIN "stats" ON 
     ("stats"."date" >= '2016-03-27'::date) 
     AND ("stats"."date" <= '2016-04-25'::date) 
     AND ("campaign"."id" = "stats"."campaign_id") 
    GROUP BY "campaign"."id" 
    ORDER BY "campaign"."status" ASC, "campaign"."created" DESC 
    LIMIT 25; 

クエリプラン:

Limit (cost=6445.26..6445.32 rows=25 width=53) (actual time=642.134..642.422 rows=25 loops=1) 
    -> Sort (cost=6445.26..6446.80 rows=617 width=53) (actual time=642.113..642.209 rows=25 loops=1) 
     Sort Key: campaign.status, campaign.created 
     Sort Method: top-N heapsort Memory: 28kB 
     -> HashAggregate (cost=6421.68..6427.85 rows=617 width=53) (actual time=634.619..637.342 rows=617 loops=1) 
       Group Key: campaign.id 
       -> Hash Right Join (cost=58.88..6269.08 rows=30519 width=53) (actual time=9.986..481.628 rows=31142 loops=1) 
        Hash Cond: (stats.campaign_id = campaign.id) 
        -> Seq Scan on stats (cost=0.00..5790.56 rows=30519 width=8) (actual time=0.044..172.346 rows=31027 loops=1) 
          Filter: ((date >= '2016-03-27'::date) AND (date <= '2016-04-25'::date)) 
          Rows Removed by Filter: 22299 
        -> Hash (cost=51.17..51.17 rows=617 width=49) (actual time=9.325..9.325 rows=617 loops=1) 
          Buckets: 1024 Batches: 1 Memory Usage: 52kB 
          -> Seq Scan on campaign (cost=0.00..51.17 rows=617 width=49) (actual time=0.043..4.490 rows=617 loops=1) 
Planning time: 1.778 ms 
Execution time: 643.217 ms 

テーブル:

          Table "public.campaign" 
     Column  |   Type   |       Modifiers       
----------------------+--------------------------+--------------------------------------------------------------- 
id     | integer     | not null default nextval('campaign_id_seq'::regclass) 
name     | character varying(255) | not null 
created    | timestamp with time zone | not null 
status    | character varying(32) | not null 
Indexes: 
    "campaign_pkey" PRIMARY KEY, btree (id) 
    "campaign_9acb4454" btree (status) 
    "campaign_9bea82de" btree (product_id) 
    "campaign_created_7aea656cce4d74c_uniq" btree (created) 
Foreign-key constraints: 
    TABLE "stats" CONSTRAINT "stats_campaign_id_dabb6227_fk_campaign_id" FOREIGN KEY (campaign_id) REFERENCES campaign(id) DEFERRABLE INITIALLY DEFERRED 


             Table "public.stats" 
    Column  |   Type   |       Modifiers       
-----------------+-------------------------+------------------------------------------------------------ 
id    | integer     | not null default nextval('stats_id_seq'::regclass) 
date   | date     | not null 
impressions  | integer     | not null 
campaign_id  | integer     | not null 
Indexes: 
    "stats_pkey" PRIMARY KEY, btree (id) 
    "stats_date_1de4ab17_uniq" btree (date) 
    "stats_f14acec3" btree (campaign_id) 
Foreign-key constraints: 
    "stats_campaign_id_dabb6227_fk_campaign_id" FOREIGN KEY (campaign_id) REFERENCES campaign(id) DEFERRABLE INITIALLY DEFERRED 

===============

編集:

条件がJOINからWHEREに移動された場合のクエリプラン:

Limit (cost=10252.48..10252.55 rows=25 width=252) (actual time=921.152..921.423 rows=25 loops=1) 
    -> Sort (cost=10252.48..10254.03 rows=617 width=252) (actual time=921.142..921.230 rows=25 loops=1) 
     Sort Key: campaign.status, campaign.created 
     Sort Method: top-N heapsort Memory: 37kB 
     -> HashAggregate (cost=10161.03..10235.07 rows=617 width=252) (actual time=910.690..916.553 rows=550 loops=1) 
       Group Key: campaign.id 
       -> Hash Right Join (cost=58.88..6575.05 rows=30519 width=252) (actual time=7.655..708.881 rows=31075 loops=1) 
        Hash Cond: (stats.campaign_id = campaign.id) 
        Filter: ((stats.date IS NULL) OR ((stats.date >= '2016-03-27'::date) AND (stats.date <= '2016-04-25'::date))) 
        Rows Removed by Filter: 22299 
        -> Seq Scan on stats (cost=0.00..5526.71 rows=52771 width=56) (actual time=0.009..249.230 rows=53326 loops=1) 
        -> Hash (cost=51.17..51.17 rows=617 width=204) (actual time=7.588..7.588 rows=617 loops=1) 
          Buckets: 1024 Batches: 1 Memory Usage: 128kB 
          -> Seq Scan on campaign (cost=0.00..51.17 rows=617 width=204) (actual time=0.009..3.124 rows=617 loops=1) 
Planning time: 0.604 ms 
Execution time: 922.323 ms 
+0

でソートしたい場合はまあ、私はPostgreSQLはインデックスを使用することができるはずだと思うことはできません日付フィールドに表示されます。 '(" stats "。" date "= '2016-03-27' :: date)AND(" stats "。" date "<= '2016-04-25': :日付) '?あなたは最近掃除をしましたか? –

+0

@ClémentPrévostそれはレコードの約50%です。日付範囲を1日に変更すると、日付のインデックスが使用されます。 Alrightは、日付インデックスがうまくいっていると仮定します。それ以外の理由は何ですか、なぜそれがインデックスに最も明白なようなキャンペーンの最後の行のseqスキャンですか?また私は手動でそれを真空にしない、私はそれが自動であるべきだと思った。 – serg

+0

それは自動です、それは確かに確かでした。 '(" stats "。" date "> = '2016-03-27' :: date)AND(" stats "。" date = "2016-04-25" ::日付) 'ジョイン句のうち? 'WHERE(stats.dateがnullまたは(" "stats"。 "date"> = '2016-03-27' :: date)AND( "stats"。 "date" <= '2016-04-25 ':: date)) '。私は、結合条件が複雑すぎて、PostgreSQLに日付フィルタが実際にフィルタであり、結合条件ではないことを理解させてくれると思います。 –

答えて

1

あなたはこのようなクエリを書いて検討するかもしれない:

SELECT c."id", c."name", c."status", 
     (SELECT SUM(s."impressions") 
     FROM "stats" s 
     WHERE c."id" = s."campaign_id" AND 
       s."date" >= '2016-03-27'::date AND 
       s."date" <= '2016-04-25'::date 
     ) as "impressions" 
FROM "campaign" c 
ORDER BY c."status" ASC, c."created" DESC ; 

その後、最適な索引がcampaign(status, created desc, name, id)stats(campaign_id, date, impressions)です。注:これらは両方とも、クエリを完全にカバーするマルチカラムインデックスです(アクセスされたすべてのカラムがインデックスにあることを意味します)。

Postgresオプティマイザは良いです。ただし、クエリの形式で外部集計を最適化するだけで十分だとは思わないでください。 ORDER BYのインデックスを使用できるため、相関サブクエリを使用するこのバージョンは、明示的なGROUP BYを使用するバージョンよりも高速です。

+0

インプレッション以外にも統計情報の列がたくさんありますが、この方法をまだ使用できますか? – serg

+0

@serg。 。 。このクエリから始め、パフォーマンスが向上するかどうかを確認します。その場合は、横方向結合を試してください。それでもうまくいくならば、追加の列を追加してください。 –

+0

列を追加するにはどうすればよいですか?計算フィールドごとにサブクエリを1つ追加しますか?それとも、私は明らかなことを見逃している。 2つのフィールドのクエリを投稿してください。または、2つのクエリでそれを行うことを意味します。まず、すべてのキャンペーンを選択し、それらのキャンペーンIDの統計情報を選択します。これは、私がいくつかの統計フィールド(インプレッションの合計など)で注文したい場合、不便になります。 – serg

1

あなたが最初に制限した場合、あなたは物事をスピードアップすることができるかもしれませんが、あなたは、あなたがstats集計

WITH top_campaign (
    SELECT * 
    FROM "campaign" 
    ORDER BY "campaign"."status" ASC, "campaign"."created" DESC 
    LIMIT 25 
) 
SELECT "campaign"."id", "campaign"."name", "campaign"."status", SUM("stats"."impressions") AS "impressions" 
FROM "top_campaign" as "campaign" 
LEFT OUTER JOIN "stats" ON ("campaign"."id" = "stats"."campaign_id") AND ("stats"."date" >= '2016-03-27'::date) AND ("stats"."date" <= '2016-04-25'::date) 
GROUP BY "campaign"."id" 
ORDER BY "campaign"."status" ASC, "campaign"."created" DESC 
関連する問題