Data MySQL - Cara terbaik untuk menerapkan paging?

209

Aplikasi iPhone saya terhubung ke layanan web PHP saya untuk mengambil data dari database MySQL. Permintaan dapat mengembalikan 500 hasil.

Apa cara terbaik untuk menerapkan paging dan mengambil 20 item sekaligus?

Katakanlah saya menerima 20 iklan pertama dari basis data saya. Sekarang bagaimana saya bisa meminta 20 iklan berikutnya?

aryaxt
sumber

Jawaban:

310

Dari dokumentasi MySQL :

Klausa LIMIT dapat digunakan untuk membatasi jumlah baris yang dikembalikan oleh pernyataan SELECT. LIMIT mengambil satu atau dua argumen numerik, yang keduanya harus berupa konstanta integer nonnegatif (kecuali saat menggunakan pernyataan yang disiapkan).

Dengan dua argumen, argumen pertama menentukan offset dari baris pertama untuk kembali, dan argumen kedua menentukan jumlah maksimum baris untuk kembali. Offset baris awal adalah 0 (bukan 1):

SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15

Untuk mengambil semua baris dari offset tertentu hingga akhir set hasil, Anda dapat menggunakan sejumlah besar untuk parameter kedua. Pernyataan ini mengambil semua baris dari baris ke-96 ke baris terakhir:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

Dengan satu argumen, nilai menentukan jumlah baris yang akan dikembalikan dari awal set hasil:

SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows

Dengan kata lain, LIMIT row_count setara dengan LIMIT 0, row_count.

Faisal Feroz
sumber
108
Saat menggunakan LIMIT untuk paging, Anda juga harus menentukan ORDER BY.
Mark Byers
10
@shylent: Tidak ada yang salah dengan mengutip dokumentasi, tetapi saya setuju bahwa dia seharusnya menyebutkan bahwa dia sedang menyalin dokumen dan memberikan tautan ke sumber aslinya. Juga saya terkejut bahwa dokumentasi akan menyertakan contoh-contoh menggunakan LIMIT tanpa PESANAN DENGAN ... yang tampaknya seperti praktik yang buruk untuk mendorong. Tanpa ORDER OLEH tidak ada jaminan bahwa pesanan akan sama antara panggilan.
Mark Byers
13
lagi pula, ketika membuat pagaging hasil besar (dan itulah gunanya pagination - memecah hasil besar menjadi potongan yang lebih kecil, bukan?), Anda harus ingat bahwa jika Anda melakukan limit X, Y, apa yang terjadi adalah bahwa baris X + Y diambil dan kemudian Baris X dari awal dijatuhkan dan apa pun yang tersisa dikembalikan. Untuk mengulangi: limit X, Yhasil dalam pemindaian baris X + Y.
shylent
7
Saya tidak suka ide LIMIT 95 Anda, 18446744073709551615 .. lihatlah OFFSET;-)
CharlesLeaf
5
Ini tidak efisien ketika bekerja dengan data besar. Periksa codular.com/implementing-pagination untuk cara mutiple yang cocok untuk scenerio tertentu.
Amit
125

Untuk 500 rekaman, efisiensi mungkin bukan masalah, tetapi jika Anda memiliki jutaan catatan, maka akan menguntungkan jika menggunakan klausa WHERE untuk memilih halaman berikutnya:

SELECT *
FROM yourtable
WHERE id > 234374
ORDER BY id
LIMIT 20

"234374" di sini adalah id dari catatan terakhir dari halaman sebelumnya yang Anda lihat.

Ini akan memungkinkan indeks id digunakan untuk menemukan catatan pertama. Jika Anda menggunakan LIMIT offset, 20Anda bisa menemukan bahwa itu semakin lambat ketika halaman Anda menuju akhir. Seperti yang saya katakan, mungkin tidak masalah jika Anda hanya memiliki 200 catatan, tetapi dapat membuat perbedaan dengan set hasil yang lebih besar.

Keuntungan lain dari pendekatan ini adalah bahwa jika data berubah di antara panggilan Anda tidak akan kehilangan catatan atau mendapatkan catatan berulang. Ini karena menambah atau menghapus baris berarti offset semua baris setelah itu berubah. Dalam kasus Anda, ini mungkin tidak penting - saya kira kumpulan iklan Anda tidak terlalu sering berubah dan tidak ada yang akan melihat jika mereka mendapatkan iklan yang sama dua kali berturut-turut - tetapi jika Anda mencari "cara terbaik" maka ini adalah hal lain yang perlu diingat ketika memilih pendekatan mana yang akan digunakan.

Jika Anda ingin menggunakan LIMIT dengan offset (dan ini diperlukan jika pengguna menavigasi langsung ke halaman 10000 alih-alih membuka halaman satu per satu) maka Anda dapat membaca artikel ini tentang pencarian baris terakhir untuk meningkatkan kinerja LIMIT dengan besar mengimbangi.

Mark Byers
sumber
1
Ini lebih seperti itu: P Walaupun saya benar-benar tidak setuju dengan implikasinya, bahwa id 'yang lebih baru' selalu lebih besar, daripada id yang 'lebih tua', sebagian besar waktu memang demikian, dan saya pikir ini bagus cukup'. Bagaimanapun, ya, seperti yang Anda tunjukkan, pagination yang tepat (tanpa degradasi kinerja yang parah pada hasil yang besar) tidak terlalu sepele dan menulis limit 1000000, 10dan berharap bahwa itu akan berhasil tidak akan membawa Anda ke mana pun.
shylent
1
tautan pencarian terlambat sangat berguna
pvgoddijn
1
Halaman ini berfungsi mundur jika Anda hanya menggunakan "DESC" untuk pemesanan id. Saya suka itu!
Dennis Heiden
2
tetapi seberapa sering orang ingin memesan dengan ID atau, dengan sindiran, dengan "tanggal dibuat" di dunia nyata?
RichieHH
posting yang baik, tapi area=width*heightsehingga tidak hanya kuantitas catatan yang mungkin penting, tetapi ukuran setiap record juga merupakan faktor ketika menyimpan hasil dalam memori
nothingisnecessary
43

Tentukan OFFSET untuk kueri. Sebagai contoh

halaman 1 - (catatan 01-10): offset = 0, batas = 10;

halaman 2 - (catatan 11-20) offset = 10, batas = 10;

dan gunakan permintaan berikut:

SELECT column FROM table LIMIT {someLimit} OFFSET {someOffset};

contoh untuk halaman 2:

SELECT column FROM table
LIMIT 10 OFFSET 10;
Prabodh Hend
sumber
1
Bukankah maksud Anda offset = 10 untuk halaman-2?
Jenna Maiz
28

Ada literatur tentang hal itu:

Masalah utama terjadi dengan penggunaan besar OFFSET. Mereka menghindari penggunaan OFFSETdengan berbagai teknik, mulai dari idpilihan rentang dalam WHEREklausa, hingga semacam caching atau halaman pra-komputasi.

Ada solusi yang disarankan di Use the INDEX, Luke :

Luchostein
sumber
1
mendapatkan id maksimum untuk setiap kueri paging dari kueri yang kompleks akan menghasilkan penggunaan yang tidak praktis, non-edukasi, peringkat, nomor baris, dan di antara tipe paging klausa membantu dalam kinerja!
Rizwan Patel
Strategi itu dipertimbangkan dan dievaluasi dengan benar dalam tautan yang disediakan. Tidak sesederhana itu sama sekali.
Luchostein
tautan yang disediakan hanya memenuhi basis pivot uni-pivot, cross apply, multi CTE atau mekanisme tabel turunan? lagi saya berdiri dengan kasus saya dengan menulis ulang pertanyaan pada besarnya seperti itu lagi untuk mendapatkan yang maksimal adalah arsitektur berlebihan! dan sekali lagi permutasi dan kombinasi untuk n "jumlah kolom dengan pesanan urut!
Rizwan Patel
1
Apakah saya salah paham bahwa tautan "Pagination dilakukan dengan cara yang benar", atau hanya tidak praktis dalam kueri yang melibatkan penyaringan.
contactmatt
1
@contactmatt, saya membagikan pemahaman Anda. Pada akhirnya, tampaknya tidak ada cara untuk mengimplementasikan secara efisien persyaratan penuh, tetapi variasi santai di sekitar aslinya.
Luchostein
13

Tutorial ini menunjukkan cara hebat untuk melakukan pagination. Pagination yang Efisien Menggunakan MySQL

Singkatnya, hindari menggunakan OFFSET atau LIMIT besar

Bao Le
sumber
24
mungkin memberikan ringkasan?
Andrew
Ya, saya akan lebih menghargai upaya dalam jawabannya.
Zorkind
6

Anda juga bisa melakukannya

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

Hitungan baris dari pernyataan pilih (tanpa batas) ditangkap dalam pernyataan pilih yang sama sehingga Anda tidak perlu menanyakan ukuran tabel lagi. Anda mendapatkan jumlah baris menggunakan SELECT FOUND_ROWS ();

surajz
sumber
1
Ini sangat tidak efisien. The *hasil lebih kolom dari makhluk yang diperlukan diambil, dan SQL_CALC_FOUND_ROWShasil dalam kolom tersebut sedang dibaca dari semua baris dalam tabel, meskipun mereka tidak termasuk dalam hasil. Akan jauh lebih efisien untuk menghitung jumlah baris dalam kueri terpisah yang tidak membaca semua kolom itu. Maka permintaan utama Anda dapat berhenti setelah membaca 20 baris.
thomasrutter
Apakah kamu yakin Saya mengatur kueri terhadap tabel besar SQL_CALC_FOUND_ROWS dan kueri lain tidak menggunakan. Saya tidak melihat perbedaan waktu. Cara apa pun itu lebih cepat daripada melakukan 2 pertanyaan. 1 - pilih * dari batas atable 0 20, lalu pilih hitung (*) dari atable.
surajz
1
Ya saya yakin - inilah info lebih lanjut . Dalam semua kasus ketika Anda menggunakan indeks untuk memfilter baris, SQL_CALC_FOUND_ROWS secara signifikan lebih lambat daripada melakukan 2 kueri yang terpisah. Pada kesempatan langka Anda tidak menggunakan indeks, atau (seperti dalam contoh sederhana ini) Anda tidak memiliki klausa WHERE dan ini adalah tabel MYISAM, itu membuat sedikit perbedaan (sekitar kecepatan yang sama).
thomasrutter
Juga inilah diskusi tentang hal itu di Stackoverflow
thomasrutter
4

Pertanyaan 1: SELECT * FROM yourtable WHERE id > 0 ORDER BY id LIMIT 500

Pertanyaan 2: SELECT * FROM tbl LIMIT 0,500;

Kueri 1 berjalan lebih cepat dengan catatan kecil atau menengah, jika jumlah catatan sama dengan 5.000 atau lebih tinggi, hasilnya serupa.

Hasil untuk 500 catatan:

Kueri1 ambil 9,9999904632568 milidetik

Kueri2 ambil 19,999980926514 milidetik

Hasil untuk 8.000 catatan:

Kueri1 ambil 129,99987602234 milidetik

Kueri2 ambil 160.00008583069 milidetik

Huy
sumber
Anda harus meletakkan indeks id.
Maarten
6
Bagaimana id > 0berguna?
Michel Jung
1
Seperti yang dikatakan Maarten, kedua pertanyaan itu pada dasarnya tampak sama, dan mungkin juga dipecah menjadi perintah tingkat mesin yang sama. Anda harus memiliki masalah pengindeksan atau versi MySQL yang benar-benar lama.
HoldOffHunger
terima kasih, seperti di saya tidak melihat jawaban Anda, saya hanya perlu melihat urutan di mana, pesanan dan batas datang
Shreyan Mehta
contoh yang salah telah digunakan. dengan offset(argumen pertama untuk membatasi diimbangi), Anda masih memilih semua data ke batas, lalu membuang jumlah offset itu, lalu mengembalikan bagian yang berada di antara offsetdan limit. dengan whereklausa di sisi lain, Anda menetapkan semacam titik awal untuk kueri dan meminta ONLYbagian tertentu.
senaps
0

Paging sederhana ketika mengambil data dari satu tabel tetapi rumit ketika mengambil data yang bergabung dengan beberapa tabel. Berikut adalah contoh yang baik dengan MySql dan Spring:
https://www.easycodeforall.com/zpagination1.jsp

Susanta Ghosh
sumber
Tolong jangan berbagi tautan ke situs pihak ketiga yang suatu hari nanti akan hilang. Jika Anda ingin menjawab pertanyaan penulis, poskan kode yang relevan untuk membantu mereka.
Manchester tanpa merek