OFFSET
yang besar akan selalu lambat. Postgres harus mengurutkan semua baris dan menghitung terlihat yang sesuai dengan offset Anda. Untuk melewati semua baris sebelumnya secara langsung anda dapat menambahkan row_number
yang diindeks ke tabel (atau buat MATERIALIZED VIEW
termasuk row_number
said ) dan bekerja dengan WHERE row_number > x
bukannya OFFSET x
.
Namun, pendekatan ini hanya masuk akal untuk data hanya-baca (atau sebagian besar). Menerapkan hal yang sama untuk data tabel yang dapat berubah bersamaan lebih menantang. Anda harus mulai dengan mendefinisikan perilaku yang diinginkan dengan tepat .
Saya menyarankan pendekatan yang berbeda untuk pagination :
SELECT *
FROM big_table
WHERE (vote, id) > (vote_x, id_x) -- ROW values
ORDER BY vote, id -- needs to be deterministic
LIMIT n;
Dimana vote_x
dan id_x
berasal dari terakhir baris laman sebelumnya (untuk keduanya DESC
dan ASC
). Atau dari pertama jika menavigasi mundur .
Membandingkan nilai baris didukung oleh indeks yang sudah Anda miliki - fitur yang sesuai dengan standar ISO SQL, tetapi tidak semua RDBMS mendukungnya.
CREATE INDEX vote_order_asc ON big_table (vote, id);
Atau untuk urutan menurun:
SELECT *
FROM big_table
WHERE (vote, id) < (vote_x, id_x) -- ROW values
ORDER BY vote DESC, id DESC
LIMIT n;
Dapat menggunakan indeks yang sama.
Saya sarankan Anda mendeklarasikan kolom Anda NOT NULL
atau kenali diri Anda dengan NULLS FIRST|LAST
membangun:
- Urutkan PostgreSQL berdasarkan datetime asc, null dulu?
Perhatikan dua hal khususnya:
-
ROW
nilai dalamWHERE
klausa tidak dapat diganti dengan bidang anggota yang terpisah.WHERE (vote, id) > (vote_x, id_x)
tidak bisa diganti dengan:WHERE vote >= vote_x AND id > id_xItu akan mengesampingkan semua baris dengan
id <= id_x
, sementara kami hanya ingin melakukannya untuk suara yang sama dan bukan untuk suara berikutnya. Terjemahan yang benar adalah:WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
... yang tidak cocok dengan indeks, dan menjadi semakin rumit untuk lebih banyak kolom.
Akan sederhana untuk lajang kolom, jelas. Itulah kasus khusus yang saya sebutkan di awal.
-
Teknik ini tidak bekerja untuk arah campuran di
ORDER BY
seperti:ORDER BY vote ASC, id DESC
Setidaknya saya tidak bisa memikirkan generik cara untuk menerapkan ini sebagai efisien. Jika setidaknya salah satu dari kedua kolom adalah tipe numerik, Anda dapat menggunakan indeks fungsional dengan nilai terbalik pada
(vote, (id * -1))
- dan gunakan ekspresi yang sama diORDER BY
:ORDER BY vote ASC, (id * -1) ASC
Terkait:
- Istilah sintaks SQL untuk 'WHERE (col1, col2) <(val1, val2)'
- Tingkatkan performa untuk pemesanan dengan kolom dari banyak tabel
Perhatikan khususnya presentasi Markus Win dan saya tautkan ke:
- "Paginasi dilakukan dengan cara PostgreSQL"