Angesichts der Tabelle:
Column | Type
id | integer
latitude | numeric(9,6)
longitude | numeric(9,6)
speed | integer
equipment_id | integer
created_at | timestamp without time zone
Indexes:
"geoposition_records_pkey" PRIMARY KEY, btree (id)
Die Tabelle enthält 20 Millionen Datensätze , was relativ gesehen keine große Anzahl ist. Aber es macht sequentielle Scans langsam.
Wie kann ich den letzten Datensatz ( max(created_at)
) von jedem equipment_id
abrufen?
Ich habe die beiden folgenden Abfragen mit mehreren Varianten ausprobiert, die ich in vielen Antworten zu diesem Thema gelesen habe:
select max(created_at),equipment_id from geoposition_records group by equipment_id;
select distinct on (equipment_id) equipment_id,created_at
from geoposition_records order by equipment_id, created_at desc;
Ich habe auch versucht, Btree-Indizes für zu erstellen, equipment_id,created_at
aber Postgres stellt fest, dass die Verwendung eines Seqscan schneller ist. Forcen enable_seqscan = off
nützt auch nichts, da das Lesen des Index so langsam ist wie der Seq-Scan, wahrscheinlich sogar noch schlimmer.
Die Abfrage muss regelmäßig ausgeführt werden und immer die letzte zurückgeben.
Verwenden von Postgres 9.3.
Erklären / analysieren (mit 1,7 Millionen Datensätzen):
set enable_seqscan=true;
explain analyze select max(created_at),equipment_id from geoposition_records group by equipment_id;
"HashAggregate (cost=47803.77..47804.34 rows=57 width=12) (actual time=1935.536..1935.556 rows=58 loops=1)"
" -> Seq Scan on geoposition_records (cost=0.00..39544.51 rows=1651851 width=12) (actual time=0.029..494.296 rows=1651851 loops=1)"
"Total runtime: 1935.632 ms"
set enable_seqscan=false;
explain analyze select max(created_at),equipment_id from geoposition_records group by equipment_id;
"GroupAggregate (cost=0.00..2995933.57 rows=57 width=12) (actual time=222.034..11305.073 rows=58 loops=1)"
" -> Index Scan using geoposition_records_equipment_id_created_at_idx on geoposition_records (cost=0.00..2987673.75 rows=1651851 width=12) (actual time=0.062..10248.703 rows=1651851 loops=1)"
"Total runtime: 11305.161 ms"
NULL
Werte imequipment_id
erwarteten Prozentsatz gibt, liegt unter 0,1%Antworten:
Ein einfacher mehrspaltiger B-Tree-Index sollte schließlich funktionieren:
Warum
DESC NULLS LAST
?Funktion
Wenn Sie keinen Sinn in den Abfrageplaner einbringen können, sollte eine Funktion, die die Ausrüstungstabelle durchläuft, den Trick tun. Wenn Sie jeweils eine equipment_id nachschlagen, wird der Index verwendet. Für eine kleine Zahl (57 nach Ihrer
EXPLAIN ANALYZE
Ausgabe) ist das schnell.Nehmen wir an, Sie haben einen
equipment
Tisch?Auch für einen netten Anruf:
Korrelierte Unterabfragen
equipment
Wenn Sie sich diese Tabelle einmal überlegen, können Sie die Drecksarbeit mit niedrig korrelierten Unterabfragen zu einem großen Erfolg machen:Die Leistung ist sehr gut.
LATERAL
Machen Sie mit bei Postgres 9.3+Ausführliche Erklärung:
Ähnliche Leistung wie die korrelierte Unterabfrage. Vergleicht man die Leistung von
max()
,DISTINCT ON
, Funktion, korrelierte Unterabfrage undLATERAL
in dieser:SQL-Geige .
quelle
Versuch 1
Wenn
equipment
Tisch undgeoposition_records(equipment_id, created_at desc)
dann funktioniert bei mir folgendes:
Ich konnte PG nicht zwingen, eine schnelle Abfrage durchzuführen, um beide Listen zu ermitteln
equipment_id
s als auch die zugehörige zu ermittelnmax(created_at)
. Aber ich werde es morgen noch einmal versuchen!Versuch 2
Ich habe diesen Link gefunden: http://zogovic.com/post/44856908222/optimizing-postgresql-query-for-distinct-values Wenn ich diese Technik mit meiner Abfrage aus Versuch 1 kombiniere, erhalte ich:
und das funktioniert SCHNELL! Aber du brauchst
geoposition_records(equipment_id, created_at desc)
.quelle