PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Optimalkan kueri dengan OFFSET di meja besar

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:

  1. ROW nilai dalam WHERE 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_x

    Itu 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.

  2. 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 di ORDER 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"


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tolok ukur Meltdown PostgreSQL

  2. Fitur Kompatibilitas Oracle baru di PostgresPlus Advanced Server 9.3Beta

  3. Bagaimana cara memasukkan kata sandi ke pg_dump?

  4. Mengurai pemutakhiran PostgreSQL

  5. Data baru tidak bertahan ke kolom array Rails di Postgres