Untuk hanya 400 stasiun, query ini akan menjadi besar-besaran lebih cepat:
SELECT s.station_id, l.submitted_at, l.level_sensor
FROM station s
CROSS JOIN LATERAL (
SELECT submitted_at, level_sensor
FROM station_logs
WHERE station_id = s.station_id
ORDER BY submitted_at DESC NULLS LAST
LIMIT 1
) l;
dbfiddle di sini
(membandingkan paket untuk kueri ini, alternatif Abelisto, dan asli Anda)
Hasil EXPLAIN ANALYZE
seperti yang disediakan oleh OP:
Nested Loop (biaya = 0,56..356,65 baris = 102 lebar = 20) (waktu aktual = 0,034..0.979 baris = 98 loop = 1)
-> Pemindaian Seq pada stasiun s (biaya = 0,00..3.02 baris = 102 lebar = 4) (waktu aktual = 0,009..0.016 baris = 102 loop = 1)
-> Batas (biaya = 0,56..3.45 baris = 1 lebar = 16) (waktu aktual = 0.009..0.009 baris = 1 loop = 102)
-> Pemindaian Indeks menggunakan station_id__submitted_at pada station_logs (biaya = 0,56..664062,38 baris = 230223 lebar = 16) (waktu aktual = 0,009 $
Indeks Cond: (station_id = s.id)
Waktu perencanaan: 0,542 ms
Waktu pelaksanaan: 1.013 ms - !!
Satu-satunya indeks yang Anda butuhkan adalah satu Anda buat: station_id__submitted_at
. The UNIQUE
kendala uniq_sid_sat
juga melakukan pekerjaan, pada dasarnya. Mempertahankan keduanya tampak seperti pemborosan ruang disk dan kinerja penulisan.
Saya menambahkan NULLS LAST
ke ORDER BY
dalam permintaan karena submitted_at
tidak ditentukan NOT NULL
. Idealnya, jika berlaku!, Tambahkan NOT NULL
kendala ke kolom submitted_at
, jatuhkan indeks tambahan dan hapus NULLS LAST
dari kueri.
Jika submitted_at
bisa NULL
, buat UNIQUE
indeks ini untuk mengganti indeks Anda saat ini dan batasan unik:
CREATE UNIQUE INDEX station_logs_uni ON station_logs(station_id, submitted_at DESC NULLS LAST);
Mempertimbangkan:
Ini mengasumsikan tabel terpisahstation
dengan satu baris per relevan station_id
(biasanya PK) - yang seharusnya Anda miliki. Jika Anda tidak memilikinya, buat itu. Sekali lagi, sangat cepat dengan teknik rCTE ini:
CREATE TABLE station AS
WITH RECURSIVE cte AS (
(
SELECT station_id
FROM station_logs
ORDER BY station_id
LIMIT 1
)
UNION ALL
SELECT l.station_id
FROM cte c
, LATERAL (
SELECT station_id
FROM station_logs
WHERE station_id > c.station_id
ORDER BY station_id
LIMIT 1
) l
)
TABLE cte;
Saya menggunakannya di biola juga. Anda bisa menggunakan kueri serupa untuk menyelesaikan tugas Anda secara langsung, tanpa station
tabel - jika Anda tidak dapat diyakinkan untuk membuatnya.
Instruksi terperinci, penjelasan dan alternatif:
Optimalkan indeks
Permintaan Anda harus sangat cepat sekarang. Hanya jika Anda masih perlu mengoptimalkan kinerja baca ...
Mungkin masuk akal untuk menambahkan level_sensor
sebagai kolom terakhir ke indeks untuk memungkinkan pemindaian hanya indeks , seperti komentar joanolo .
Con: Itu membuat indeks lebih besar - yang menambahkan sedikit biaya untuk semua permintaan menggunakannya.
Pro: Jika Anda benar-benar mendapatkan hanya scan indeks dari itu, permintaan di tangan tidak harus mengunjungi halaman tumpukan sama sekali, yang membuatnya sekitar dua kali lebih cepat. Tapi itu mungkin keuntungan yang tidak substansial untuk permintaan yang sangat cepat sekarang.
Namun , saya tidak berharap itu bekerja untuk kasus Anda. Anda menyebutkan:
... sekitar 20rb baris per hari per station_id
.
Biasanya, itu akan menunjukkan beban tulis tanpa henti (1 per station_id
setiap 5 detik). Dan Anda tertarik dengan baris terbaru . Pemindaian hanya indeks hanya berfungsi untuk menumpuk halaman yang terlihat oleh semua transaksi (bit dalam peta visibilitas diatur). Anda harus menjalankan VACUUM
pengaturan yang sangat agresif agar tabel dapat mengikuti beban penulisan, dan sebagian besar waktu tidak akan berfungsi. Jika asumsi saya benar, hanya pemindaian indeks keluar, jangan tambahkan level_sensor
ke indeks.
OTOH, jika asumsi saya bertahan, dan meja Anda tumbuh sangat besar , indeks BRIN mungkin membantu. Terkait:
Atau, yang lebih terspesialisasi dan lebih efisien: Indeks parsial hanya untuk penambahan terbaru untuk memotong sebagian besar baris yang tidak relevan:
CREATE INDEX station_id__submitted_at_recent_idx ON station_logs(station_id, submitted_at DESC NULLS LAST)
WHERE submitted_at > '2017-06-24 00:00';
Pilih stempel waktu yang Anda tahu bahwa baris yang lebih muda harus ada. Anda harus menambahkan WHERE
kondisi yang cocok ke semua permintaan, seperti:
...
WHERE station_id = s.station_id
AND submitted_at > '2017-06-24 00:00'
...
Anda harus menyesuaikan indeks dan kueri dari waktu ke waktu.
Jawaban terkait dengan detail lebih lanjut: