Ich habe zwei Tabellen: campaign
und stats
. stats
Tabelle enthält tägliche Statistiken, die ich für jede Kampagne sammeln möchte, nichts Außergewöhnliches.Warum ignoriert Postgres alle meine Indizes für bedingte Joins?
Ich indizierte alle Felder, die ich mir vorstellen kann, aber keiner der Indizes wird von dem verwendet, was ich sagen kann. Ich weiß, dass Postgres sich dafür entscheiden könnte, keinen Index zu verwenden, aber es sieht immer noch verdächtig aus, und die Abfrage ist auch nicht blitzschnell. Wie kann ich helfen?
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;
Abfrage-Plan:
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
Tables:
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
===============
Edit:
Abfrageplan, wenn die Bedingung aus JOIN in WHERE verschoben wird:
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
Nun, ich denke PostgreSQL Lage sein sollte, einen Index zu verwenden auf dem Datumsfeld. Was ist der Anteil von 'stats' ausgewählt mit' ("stats". "Date"> = '2016-03-27' :: date) UND ("stats". "Date" <= '2016-04-25': : Datum) '? Hast du kürzlich Staubsauger? –
@ ClémentPrévost Es sind ungefähr 50% der Datensätze. Wenn ich den Datumsbereich auf 1 Tag ändere, wird das Indexdatum verwendet. Alles klar, nehmen wir an, der Datumsindex ist in Ordnung, warum sonst nichts, warum macht es die Seq-Scan-Kampagne (letzte Zeile im Plan), die am offensichtlichsten zu indexieren scheint? Auch ich sauge es nicht manuell, ich dachte, es sollte automatisch sein. – serg
Es ist automatisch, das war nur um sicherzustellen. Ändert das etwas, wenn Sie das '(" stats "." Date "> = '2016-03-27' :: date) UND (" stats "." Date "<= '2016-04-25' :: Datum) 'aus der Join-Klausel heraus? Etwas wie 'WHERE (stats.date ist Null ODER ((" Statistik "." Datum "> =" 2016-03-27 ":: Datum) UND (" Statistik "." Datum "<=" 2016-04-25 ":: date))." Ich denke, dass die Join-Bedingung zu komplex ist, um PostgreSQL verständlich zu machen, dass der Datumsfilter tatsächlich ein Filter und keine Join-Bedingung ist. –