Ich versuche, die Leistung für die folgende Abfrage zu verbessern. Unabhängig davon, wie ich die Abfrage schreibe (Unterabfrage in der FROM-Klausel, Unterabfrage in der WHERE-Klausel), besteht postgres darauf, alle ~ 570K-Zeilen durch die teure ST_DWITHIN-Funktion auszuführen, obwohl es nur 60 Zeilen gibt, in denen county = 24 ist. Wie kann ich Postgres dazu bringen, nach County = 24 zu filtern, bevor ich die Postgis-Funktion durchlaufe, die meiner Meinung nach viel schneller und weitaus effizienter ist? 700 ms sind kein Grund zur Sorge, aber da diese Tabelle auf 10 Millionen + anwächst, mache ich mir Sorgen um die Leistung.
Zu beachten ist auch, dass p.id ein Primärschlüssel ist, p.zipcode ein fk-Index ist, z.county ein fk-Index ist und p.geom einen GiST-Index hat.
Abfrage:
EXPLAIN ANALYZE
SELECT count(p.id)
FROM point AS p
LEFT JOIN zipcode AS z
ON p.zipcode = z.zipcode
WHERE z.county = 24
AND ST_DWithin(
p.geom,
ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269),
16090.0,
false
)
ANALYSE ERKLÄREN:
Aggregate (cost=250851.91..250851.92 rows=1 width=4) (actual time=724.007..724.007 rows=1 loops=1)
-> Hash Join (cost=152.05..250851.34 rows=228 width=4) (actual time=0.359..723.996 rows=51 loops=1)
Hash Cond: ((p.zipcode)::text = (z.zipcode)::text)
-> Seq Scan on point p (cost=0.00..250669.12 rows=7437 width=10) (actual time=0.258..723.867 rows=63 loops=1)
Filter: (((geom)::geography && '0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography) AND ('0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography && _st_expand((geom)::geography, 16090::double precision)) AND _st_dwithin((g (...)
Rows Removed by Filter: 557731
-> Hash (cost=151.38..151.38 rows=54 width=6) (actual time=0.095..0.095 rows=54 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 3kB
-> Bitmap Heap Scan on zipcode z (cost=4.70..151.38 rows=54 width=6) (actual time=0.023..0.079 rows=54 loops=1)
Recheck Cond: (county = 24)
Heap Blocks: exact=39
-> Bitmap Index Scan on fki_zipcode_county_foreign_key (cost=0.00..4.68 rows=54 width=0) (actual time=0.016..0.016 rows=54 loops=1)
Index Cond: (county = 24)
Planning time: 0.504 ms
Execution time: 724.064 ms
point
Zeilen, in denen Grafschaft = 24 ist, alleine in eine neue Tabelle kopiere, dauert die Abfrage nur 0,453 ms im Vergleich zu 724, sodass es definitiv einen großen Unterschied gibt.count(*)
als eine Frage des Stils verwenden. Wennid
es sich um ein pkid handelt, wie Sie sagen,NOT NULL
bedeutet dies, dass sie gleich sind. Außercount(id)
hat den Nachteil, dass Sie diese Frage stellen müssen, wennid
nullbar ist.Antworten:
Sie können das Problem mit den erwarteten und tatsächlichen Zeilenzahlen sehen. Der Planer glaubt, dass es 7.437 Zeilen gibt, aber nur 63. Die Statistiken sind aus. Interessanterweise wird auch keine Bounding-Box-Indexsuche (Indexsuche) verwendet, mit der
DWithin
Sie das Ergebnis einfügen können\d point
. Welche Version von PostGIS und PostgreSQL?Versuche zu rennen
ANALYZE point
. Erhalten Sie den gleichen Plan, wenn Sie die Bedingung nach oben verschieben?quelle
Als Randnotiz besteht eine vernünftige Wahrscheinlichkeit, dass dieses Verhalten in PostGIS 2.3.0 geändert wird, wenn Sie es als Fehler bezeichnen möchten.
Aus den Dokumenten zu PostgreSQL
Die Standardkosten waren also 1 (sehr billig).
D_Within
Die Verwendung eines GIST-Index ist sehr billig. Dies wurde jedoch auf 100 erhöht (durch Vertretung des internen_ST_DWithin
).Ich bin selbst kein großer Fan der CTE-Methode. CTEs sind ein Optimierungszaun. Wenn Sie dies auf diese Weise tun, wird potenzieller Raum für zukünftige Optimierungen beseitigt. Wenn saner Standardeinstellungen das Problem beheben, würde ich lieber ein Upgrade durchführen. Am Ende des Tages müssen wir die Arbeit erledigen und diese Methode funktioniert eindeutig für Sie.
quelle
Dank des Hinweises von John Powell habe ich die Abfrage überarbeitet, um die County-Begrenzungsbedingung in eine With / CTE-Abfrage zu setzen, und diese verbesserte die Leistung um einiges auf 222 ms gegenüber 700. Immer noch weit entfernt von den 0,74 ms, die ich erhalte, wenn die Daten vorhanden sind eigener Tisch. Ich bin mir immer noch nicht sicher, warum der Planer den Datensatz nicht einschränkt, bevor er eine teure Postgis-Funktion durchläuft, und ich muss es mit größeren Datensätzen versuchen, wenn ich sie habe, aber dies scheint vorerst eine Lösung für diese einzigartige Situation zu sein.
quelle
Sie sollten einen Index für erstellen
zipcode(county, zipcode)
, der Ihnen einen Index-Scan nur für z geben soll.Sie können auch mit experimentieren
btree_gist
Erweiterung der Erstellung entwederpoint(zipcode, geom)
Index oderpoint(geom, zipcode)
undzipcode(zipcode, county)
Index.quelle