Dati MySQL - Il modo migliore per implementare il paging?


209

La mia app per iPhone si collega al mio servizio web PHP per recuperare dati da un database MySQL. Una richiesta può restituire 500 risultati.

Qual è il modo migliore per implementare il paging e recuperare 20 elementi alla volta?

Diciamo che ricevo i primi 20 annunci dal mio database. Ora come posso richiedere i prossimi 20 annunci?

Risposte:


310

Dalla documentazione di MySQL :

La clausola LIMIT può essere utilizzata per limitare il numero di righe restituite dall'istruzione SELECT. LIMIT accetta uno o due argomenti numerici, che devono essere entrambi costanti intere non negative (tranne quando si usano istruzioni preparate).

Con due argomenti, il primo argomento specifica l'offset della prima riga da restituire e il secondo specifica il numero massimo di righe da restituire. L'offset della riga iniziale è 0 (non 1):

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

Per recuperare tutte le righe da un determinato offset fino alla fine del set di risultati, è possibile utilizzare un numero elevato per il secondo parametro. Questa istruzione recupera tutte le righe dalla 96a riga all'ultima:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

Con un argomento, il valore specifica il numero di righe da restituire dall'inizio del set di risultati:

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

In altre parole, LIMIT row_count è equivalente a LIMIT 0, row_count.


108
Quando si utilizza LIMIT per il paging, è necessario specificare anche un ORDER BY.
Mark Byers,

10
@shylent: niente di sbagliato nel citare la documentazione, ma sono d'accordo che avrebbe dovuto menzionare che stava copiando i documenti e fornito un collegamento alla fonte originale. Inoltre sono sorpreso che la documentazione includa esempi di utilizzo di LIMIT senza un ORDER BY ... che sembra una cattiva pratica incoraggiante. Senza un ORDER BY non esiste alcuna garanzia che l'ordine sarà lo stesso tra le chiamate.
Mark Byers,

13
in ogni caso, quando impagini grandi set di risultati (ed è per questo che impagina - suddividere grandi set di risultati in blocchi più piccoli, giusto?), dovresti tenere a mente che se fai un limit X, Y, ciò che accade essenzialmente è che le righe X + Y vengono recuperate e quindi X righe dall'inizio vengono eliminate e viene restituito tutto ciò che è rimasto. Per ripetere: limit X, Yrisultati della scansione di righe X + Y.
Shylent

7
Non mi piace la tua idea LIMIT 95, 18446744073709551615 .. dai un'occhiata a OFFSET;-)
CharlesLeaf

5
Questo non è efficiente quando si lavora con dati di grandi dimensioni. Controlla codular.com/implementing-pagination per i modi multipli in cui sono adatti per scenari specifici.
Entro il

125

Per 500 record l'efficienza non è probabilmente un problema, ma se si dispone di milioni di record, può essere vantaggioso utilizzare una clausola WHERE per selezionare la pagina successiva:

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

Il "234374" qui è l'id dell'ultimo record della pagina precedente che hai visualizzato.

Ciò consentirà di utilizzare un indice su id per trovare il primo record. Se lo usi LIMIT offset, 20potresti scoprire che diventa sempre più lento man mano che scorri verso la fine. Come ho detto, probabilmente non importa se hai solo 200 record, ma può fare la differenza con set di risultati più grandi.

Un altro vantaggio di questo approccio è che se i dati cambiano tra le chiamate non perderai i record o otterrai un record ripetuto. Questo perché l'aggiunta o la rimozione di una riga significa che l'offset di tutte le righe dopo la modifica. Nel tuo caso probabilmente non è importante - immagino che il tuo pool di annunci non cambi troppo spesso e comunque nessuno noterebbe se ottengono lo stesso annuncio due volte di seguito - ma se stai cercando il "modo migliore" allora questa è un'altra cosa da tenere a mente quando si sceglie quale approccio usare.

Se si desidera utilizzare LIMIT con un offset (e questo è necessario se un utente naviga direttamente alla pagina 10000 invece di eseguire il paging attraverso le pagine una per una), è possibile leggere questo articolo sulle ricerche in ritardo per migliorare le prestazioni di LIMIT con un grande compensare.


1
Questo è più simile: P Mentre disapprovo assolutamente l'implicazione, che gli ID "più recenti" sono sempre più grandi, rispetto a quelli "più vecchi", il più delle volte questo sarà effettivamente il caso e quindi, penso, questo è "buono" abbastanza'. Comunque, sì, come hai dimostrato, una corretta impaginazione (senza un grave peggioramento delle prestazioni su grandi set di risultati) non è particolarmente banale e la scritturalimit 1000000, 10 e sperare che funzionerà non ti porterà da nessuna parte.
shylent,

1
il link di ricerca tardiva è molto utile
pvgoddijn

1
Questa impaginazione funziona all'indietro se si utilizza semplicemente "DESC" per l'ordinamento ID. Mi piace!
Dennis Heiden

2
ma quante volte le persone vogliono ordinare per ID o, per insinuazione, per "data di creazione" nel mondo reale?
RichieHH,

buon post, ma area=width*heightquindi non è solo la quantità di record che potrebbe importare, ma la dimensione di ogni record è anche un fattore importante per la memorizzazione dei risultati in memoria
nulla è necessario il

43

Definire OFFSET per la query. Per esempio

pagina 1 - (record 01-10): offset = 0, limite = 10;

pagina 2 - (record 11-20) offset = 10, limite = 10;

e usa la seguente query:

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

esempio per pagina 2:

SELECT column FROM table
LIMIT 10 OFFSET 10;

1
Non intendi offset = 10 per pagina-2?
Jenna Maiz,

28

C'è letteratura a riguardo:

Il problema principale si verifica con l'uso di grandi dimensioni OFFSET. Evitano l'uso OFFSETcon una varietà di tecniche, che vanno daid selezioni di intervallo nella WHEREclausola, a qualche tipo di cache o pagine di pre-calcolo.

Ci sono soluzioni suggerite su Usa INDICE, Luke :


1
l'ottenimento di un ID massimo per ogni query di paging di query complesse comporterebbe un utilizzo non pratico, la non produzione, il grado, il numero di riga e il tipo di paging tra clausole aiutano in performace!
Rizwan Patel,

Tale strategia è presa in considerazione e adeguatamente valutata nei collegamenti forniti. Non è affatto così semplice.
Luchostein,

il collegamento fornito sembra soddisfare solo i pivot di base uni-pivot, cross apply, multi CTE o la meccanica della tabella derivata? ancora una volta sostengo il mio caso con la riscrittura di domande di tale portata di nuovo per ottenere il massimo è eccessivo architettonico! e poi ancora permutazione e combinazione per n "numero di colonne con ordinamenti!
Rizwan Patel

1
Sto fraintendendo il collegamento "Impaginazione fatta nel modo giusto" o è semplicemente poco pratico in qualsiasi query che implichi il filtraggio.
contactmatt

1
@contactmatt Condivido la tua apprensione. Alla fine, sembra che non ci sia modo di implementare in modo efficiente l'intero requisito, ma ha rilassato le variazioni rispetto all'originale.
Luchostein,


6

puoi anche fare

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

Il conteggio delle righe dell'istruzione select (senza il limite) viene acquisito nella stessa istruzione select in modo da non dover eseguire nuovamente la query sulla dimensione della tabella. Ottieni il conteggio delle righe usando SELECT FOUND_ROWS ();


1
Questo è particolarmente inefficiente. I *risultati in più colonne del necessario vengono recuperati e i SQL_CALC_FOUND_ROWSrisultati in tali colonne vengono letti da tutte le righe della tabella, anche se non sono inclusi nel risultato. Sarebbe molto più efficiente calcolare il numero di righe in una query separata che non legge tutte quelle colonne. Quindi la query principale può arrestarsi dopo aver letto 20 righe.
thomasrutter,

Sei sicuro? Ho cronometrato la query su una grande tabella SQL_CALC_FOUND_ROWS e un'altra query non utilizzata. Non ho visto differenze di tempo. In ogni caso è più veloce di fare 2 query. 1 - selezionare * da atable limit 0 20, quindi selezionare count (*) da atable.
surajz,

1
Sì, ne sono sicuro: ecco maggiori informazioni . In tutti i casi quando si utilizza un indice per filtrare le righe, SQL_CALC_FOUND_ROWS è significativamente più lento rispetto all'esecuzione di 2 query separate. In rare occasioni non si utilizza un indice o (come in questo esempio semplificato) non si dispone di alcuna clausola WHERE ed è una tabella MYISAM, fa poca differenza (è circa alla stessa velocità).
thomasrutter,


4

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

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

La query 1 viene eseguita più rapidamente con record piccoli o medi, se il numero di record è pari o superiore a 5.000, il risultato è simile.

Risultato per 500 record:

La query1 richiede 9.9999904632568 millisecondi

Query2 richiede 19.999980926514 millisecondi

Risultato per 8.000 record:

La query1 richiede 129.99987602234 millisecondi

La query2 richiede 160,00008583069 millisecondi


Devi inserire un indice id.
Maarten,

6
Quanto è id > 0utile?
Michel Jung,

1
Come ha detto Maarten, queste due query sembrano fondamentalmente uguali e probabilmente si dividono in entrambi gli stessi comandi a livello di macchina. Devi avere un problema di indicizzazione o una versione molto vecchia di MySQL.
HoldOffHunger,

grazie, visto che non ho visto la tua risposta, ho solo bisogno di vedere l'ordine in cui arrivano l'ordine e il limite
Shreyan Mehta,

è stato usato un esempio sbagliato. con offset(il primo argomento da limitare è l'offset), stai ancora selezionando tutti i dati al limite, quindi scartando quella quantità dell'offset, quindi restituendo la sezione tra offsete limit. con la whereclausola d'altra parte, si sta impostando una specie di punto di partenza per la query e interrogare ONLYquella parte specifica.
senaps,

0

Il paging è semplice quando recupera i dati da una singola tabella ma è complesso quando recupera i dati che uniscono più tabelle. Ecco un buon esempio con MySql e Spring:
https://www.easycodeforall.com/zpagination1.jsp


Si prega di non condividere collegamenti a siti di terzi che un giorno potrebbero scomparire. Se stai cercando di rispondere alla domanda degli autori, pubblica il codice pertinente per assisterli.
Manchester senza marchio il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.