Diberikan tabel:
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)
Tabel ini memiliki 20 juta catatan yang secara relatif tidak banyak. Tapi itu membuat pemindaian berurutan lambat.
Bagaimana saya bisa mendapatkan catatan terakhir ( max(created_at)
) dari masing-masing equipment_id
?
Saya sudah mencoba kedua pertanyaan berikut, dengan beberapa varian yang telah saya baca melalui banyak jawaban dari topik ini:
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;
Saya juga telah mencoba membuat indeks btree untuk equipment_id,created_at
tetapi Postgres menemukan bahwa menggunakan seqscan lebih cepat. Pemaksaan enable_seqscan = off
tidak ada gunanya karena membaca indeks selambat pemindaian seq, mungkin lebih buruk.
Permintaan harus berjalan secara berkala, selalu kembali yang terakhir.
Menggunakan Postgres 9.3.
Jelaskan / analisis (dengan 1,7 juta catatan):
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
nilai dalamequipment_id
persentase yang diharapkan di bawah 0,1%Jawaban:
Indeks b-tree multicolumn polos seharusnya bisa digunakan:
Mengapa
DESC NULLS LAST
?Fungsi
Jika Anda tidak dapat berbicara masuk akal ke perencana kueri, perulangan fungsi melalui tabel peralatan harus melakukan trik. Mencari satu equipment_id sekaligus menggunakan indeks. Untuk sejumlah kecil (57 menilai dari
EXPLAIN ANALYZE
output Anda ), itu cepat.Aman untuk menganggap Anda memiliki
equipment
meja?Membuat panggilan yang bagus juga:
Subquery terkait
Kalau dipikir-pikir itu, menggunakan
equipment
tabel ini , Anda bisa ke pekerjaan kotor dengan subqueries berkorelasi rendah untuk efek besar:Performanya sangat bagus.
LATERAL
bergabung dengan Postgres 9.3+Penjelasan detail:
Kinerja serupa dengan subquery yang berkorelasi. Membandingkan kinerja
max()
,DISTINCT ON
, fungsi, berkorelasi subquery danLATERAL
dalam hal ini:SQL Fiddle .
sumber
Percobaan 1
Jika
equipment
meja terpisah , dangeoposition_records(equipment_id, created_at desc)
maka berikut ini berfungsi untuk saya:
Saya tidak bisa memaksa PG untuk melakukan query cepat untuk menentukan kedua daftar
equipment_id
dan terkaitmax(created_at)
. Tapi saya akan coba lagi besok!Percobaan 2
Saya menemukan tautan ini: http://zogovic.com/post/44856908222/optimizing-postgresql-query-for-distinct-values Menggabungkan teknik ini dengan permintaan saya dari upaya 1, saya dapatkan:
dan ini bekerja CEPAT! Tapi kamu butuh
geoposition_records(equipment_id, created_at desc)
.sumber