Datos MySQL: ¿la mejor manera de implementar la paginación?


209

La aplicación de mi iPhone se conecta a mi servicio web PHP para recuperar datos de una base de datos MySQL. Una solicitud puede devolver 500 resultados.

¿Cuál es la mejor manera de implementar la paginación y recuperar 20 elementos a la vez?

Digamos que recibo los primeros 20 anuncios de mi base de datos. Ahora, ¿cómo puedo solicitar los próximos 20 anuncios?

Respuestas:


310

De la documentación de MySQL :

La cláusula LIMIT puede usarse para restringir el número de filas devueltas por la instrucción SELECT. LIMIT toma uno o dos argumentos numéricos, que deben ser constantes enteras no negativas (excepto cuando se usan declaraciones preparadas).

Con dos argumentos, el primer argumento especifica el desplazamiento de la primera fila para devolver, y el segundo especifica el número máximo de filas para devolver. El desplazamiento de la fila inicial es 0 (no 1):

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

Para recuperar todas las filas desde un cierto desplazamiento hasta el final del conjunto de resultados, puede usar un número grande para el segundo parámetro. Esta declaración recupera todas las filas desde la fila 96 hasta la última:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

Con un argumento, el valor especifica el número de filas a devolver desde el comienzo del conjunto de resultados:

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

En otras palabras, LIMIT row_count es equivalente a LIMIT 0, row_count.


108
Cuando use LIMIT para paginación, también debe especificar un ORDER BY.
Mark Byers

10
@shylent: No hay nada malo en citar la documentación, pero estoy de acuerdo en que debería haber mencionado que estaba copiando los documentos y proporcionó un enlace a la fuente original. También me sorprende que la documentación incluya ejemplos del uso de LIMIT sin un ORDER BY ... que parece una mala práctica alentadora. Sin un ORDER BY no hay garantía de que el pedido sea el mismo entre llamadas.
Mark Byers

13
de todos modos, al paginar grandes conjuntos de resultados (y para eso está la paginación: divida los grandes conjuntos de resultados en trozos más pequeños, ¿no?), debe tener en cuenta que si hace un limit X, Y, lo que sucede esencialmente es que se recuperan las filas X + Y y luego Se eliminan las filas X desde el principio y se devuelve lo que queda. Para reiterar: limit X, Yresulta en el escaneo de filas X + Y.
shylent

77
No me gusta tu idea LIMIT 95, 18446744073709551615 ... mira OFFSET;-)
CharlesLeaf

55
Esto no es eficiente cuando se trabaja con datos grandes. Visite codular.com/implementing-pagination para ver varias formas adecuadas para un escenario específico.
Amit

125

Para 500 registros, la eficiencia probablemente no sea un problema, pero si tiene millones de registros, puede ser ventajoso usar una cláusula WHERE para seleccionar la página siguiente:

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

El "234374" aquí es la identificación del último registro de la página anterior que ha visto.

Esto permitirá utilizar un índice en la identificación para encontrar el primer registro. Si lo usa LIMIT offset, 20, podría encontrar que se vuelve más y más lento a medida que avanza hacia el final. Como dije, probablemente no importará si tiene solo 200 registros, pero puede marcar la diferencia con conjuntos de resultados más grandes.

Otra ventaja de este enfoque es que si los datos cambian entre las llamadas, no perderá registros ni obtendrá un registro repetido. Esto se debe a que agregar o eliminar una fila significa que el desplazamiento de todas las filas después de que cambia. En su caso, probablemente no sea importante: supongo que su grupo de anuncios no cambia con demasiada frecuencia y de todos modos nadie se daría cuenta si reciben el mismo anuncio dos veces seguidas, pero si está buscando la "mejor manera" entonces esto es otra cosa a tener en cuenta al elegir qué enfoque usar.

Si desea utilizar LIMIT con un desplazamiento (y esto es necesario si un usuario navega directamente a la página 10000 en lugar de desplazarse por las páginas una por una), puede leer este artículo sobre búsquedas de última fila para mejorar el rendimiento de LIMIT con un gran compensar.


1
Esto es más como esto: P Aunque desapruebo absolutamente la implicación, que los identificadores 'más nuevos' son siempre más grandes que los 'antiguos', la mayoría de las veces este será el caso y, por lo tanto, creo que esto es 'bueno' suficiente'. De todos modos, sí, como lo demostró, la paginación adecuada (sin una degradación severa del rendimiento en grandes conjuntos de resultados) no es particularmente trivial y escribir limit 1000000, 10y esperar que funcione no lo llevará a ninguna parte.
shylent

1
el enlace de búsqueda tardía es muy útil
pvgoddijn

1
Esta paginación funciona al revés si solo usa "DESC" para ordenar la identificación. ¡Me gusta!
Dennis Heiden

2
pero ¿con qué frecuencia las personas desean ordenar por identificación o, por insinuación, por "fecha de creación" en el mundo real?
RichieHH

buena publicación, pero area=width*heightno solo importa la cantidad de registros, sino que el tamaño de cada registro también es un factor cuando se almacenan los resultados en la memoria
nada

43

Defina DESPLAZAMIENTO para la consulta. Por ejemplo

página 1 - (registros 01-10): desplazamiento = 0, límite = 10;

página 2 - (registros 11-20) desplazamiento = 10, límite = 10;

y use la siguiente consulta:

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

ejemplo para la página 2:

SELECT column FROM table
LIMIT 10 OFFSET 10;

1
¿No quieres decir offset = 10 para la página 2?
Jenna Maiz

28

Hay literatura al respecto:

El principal problema ocurre con el uso de grandes OFFSETs. Evitan usar OFFSETcon una variedad de técnicas, que van desde idselecciones de rango enWHERE cláusula, hasta algún tipo de almacenamiento en caché o páginas de precomputación.

Hay soluciones sugeridas en Use the INDEX, Luke :


1
obtener el ID máximo para cada consulta de paginación de consultas complejas daría como resultado que el uso no práctico y sin producción sí clasifica, el número de fila y entre el tipo de cláusula de paginación ayuda en el rendimiento.
Rizwan Patel

Esa estrategia se tiene en cuenta y se evalúa adecuadamente en los enlaces proporcionados. No es tan simple en absoluto.
Luchostein

el enlace proporcionado solo parece cumplir con el pivote base uni-pivote, aplicación cruzada, CTE múltiple o mecánica de tabla derivada? Una vez más, defiendo mi caso con la reescritura de consultas de tal magnitud nuevamente para obtener el máximo es una exageración arquitectónica. y luego otra vez permutación y combinación para n "número de columna con órdenes de clasificación!
Rizwan Patel

1
¿Estoy malinterpretando ese enlace "Paginación hecha de la manera correcta", o simplemente no es práctico en cualquier consulta que implique filtrado?
contactmatt

1
@contactmatt Comparto su aprensión. Al final, parece que no hay forma de implementar eficientemente el requisito completo, sino variaciones relajadas alrededor del original.
Luchostein


6

también puedes hacer

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

El recuento de filas de la instrucción de selección (sin el límite) se captura en la misma instrucción de selección para que no necesite consultar nuevamente el tamaño de la tabla. Obtiene el recuento de filas usando SELECT FOUND_ROWS ();


1
Esto es particularmente ineficiente. Los *resultados en más columnas de las necesarias se SQL_CALC_FOUND_ROWSobtienen , y los resultados en esas columnas se leen de todas las filas de la tabla, aunque no se incluyan en el resultado. Sería mucho más eficiente calcular el número de filas en una consulta separada que no lee todas esas columnas. Entonces su consulta principal puede detenerse después de leer 20 filas.
thomasrutter

¿Estás seguro? Tomé el tiempo de la consulta en una tabla grande SQL_CALC_FOUND_ROWS y otra consulta que no estaba usando. No vi diferencia horaria. De cualquier manera, es más rápido que hacer 2 consultas. 1 - seleccione * del límite de tabla 0 20, y luego seleccione count (*) de la tabla.
surajz

1
Sí, estoy seguro, aquí hay más información . En todos los casos, cuando está utilizando un índice para filtrar filas, SQL_CALC_FOUND_ROWS es significativamente más lento que hacer 2 consultas separadas. En las raras ocasiones, no está utilizando un índice o (como en este ejemplo simplificado) no tiene una cláusula WHERE y es una tabla MYISAM, hace poca diferencia (es aproximadamente la misma velocidad).
thomasrutter

También aquí hay una discusión al respecto en
Stackoverflow

4

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

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

La consulta 1 se ejecuta más rápido con registros pequeños o medianos, si el número de registros es igual a 5,000 o más, el resultado es similar.

Resultado para 500 registros:

Consulta1 toma 9.9999904632568 milisegundos

Consulta2 toma 19.999980926514 milisegundos

Resultado para 8,000 registros:

Consulta1 toma 129.99987602234 milisegundos

Consulta2 toma 160.00008583069 milisegundos


Necesitas poner un índice id.
Maarten

66
¿Cómo es id > 0útil?
Michel Jung

1
Como dijo Maarten, esas dos consultas parecen ser básicamente las mismas, y probablemente se dividen en los mismos comandos a nivel de máquina de cualquier manera. Debe tener un problema de indexación o una versión muy antigua de MySQL.
HoldOffHunger

gracias, como en i no vi su respuesta, sólo necesitaba para ver el orden en que cuando, el orden y límite viene
Shreyan Mehta

Se ha usado un ejemplo incorrecto. con offset(el primer argumento para limitar es el desplazamiento), todavía está seleccionando todos los datos al límite, luego descarta esa cantidad del desplazamiento y luego devuelve la sección que está entre offsety limit. con la wherecláusula, por otro lado, está estableciendo una especie de punto de inicio para la consulta y consulta ONLYesa parte específica.
senaps

0

La paginación es simple cuando recupera datos de una sola tabla, pero es compleja cuando recupera datos que unen varias tablas. Aquí hay un buen ejemplo con MySql y Spring:
https://www.easycodeforall.com/zpagination1.jsp


No comparta enlaces a sitios de terceros que algún día puedan desaparecer. Si está buscando responder la pregunta de los autores, publique el código relevante para ayudarlos.
Manchester sin marca
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.