2010-09-26 14 views
126

Mi aplicación para iPhone se conecta a mi servicio web PHP para recuperar datos de una base de datos MySQL. Una solicitud puede devolver 500 resultados.Datos MySQL - ¿La mejor manera de implementar la paginación?

¿Cuál es la mejor forma de implementar 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?

Respuesta

192

From the MySQL documentation:

La cláusula LIMIT puede usarse para limitar 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 a devolver, y el segundo especifica el número máximo de filas a devolver. El desplazamiento del registro inicial es 0 (no 1):

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

Para recuperar todas las filas del desplazamiento hasta el final del conjunto de resultados, puede utilizar algún número grande para el segundo parámetro. Esta instrucción recupera todas las filas de la fila 96a de la última:

SELECT * FROM tbl LIMIT 95,18446744073709551615; 

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

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

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

+60

Al utilizar LÍMITE para paginación también se debe especificar un ORDER BY. –

+6

@shylent: No hay nada de 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 de uso de LIMIT sin un ORDER BY ... que parezca una mala práctica para ser alentador. Sin un ORDER BY no hay garantía de que el pedido será el mismo entre llamadas. –

+7

de todos modos, al paginar grandes conjuntos de resultados (y para eso sirve la paginación -desglose los grandes conjuntos de resultados en fragmentos más pequeños, ¿no?), Debe tener en cuenta que si hace un 'límite X, Y', lo que esencialmente ocurre es que X Se recuperan + filas Y y luego se descartan X filas desde el principio y se devuelve lo que queda. Para reiterar: 'límite X, Y' resultados en escaneo de filas X + Y. – shylent

3

también se puede hacer

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20 

El número de filas de la instrucción de selección (sin límite) es capturado en la misma instrucción SELECT de manera que no es necesario consultar el tamaño de la mesa de nuevo . Obtienes el recuento de filas usando SELECT FOUND_ROWS();

+1

Esto es particularmente ineficiente. El '*' da como resultado la obtención de más columnas de las necesarias, y el resultado 'SQL_CALC_FOUND_ROWS' hace que esas columnas se lean desde * todas * filas en la tabla, aunque no estén incluidas 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

+0

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

+1

Sí, estoy seguro - [aquí hay más información] (http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/). En todos los casos, cuando usa un índice para filtrar filas, SQL_CALC_FOUND_ROWS es significativamente más lento que hacer 2 consultas por separado. En la rara ocasión en que no está usando un índice, o (como en este ejemplo simplificado) no tiene una cláusula WHERE y es una tabla MYISAM, hace poca diferencia (tiene la misma velocidad). – thomasrutter

78

Por 500 eficiencia registros probablemente no es un problema, pero si usted tiene millones de discos, entonces puede ser ventajoso utilizar una cláusula WHERE para seleccionar la siguiente página:

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 de prevous que viste.

Esto permitirá que se use un índice en la identificación para buscar el primer registro. Si usa LIMIT offset, 20, puede encontrar que se vuelve más y más lento a medida que avanza hacia el final. Como dije, probablemente no importará si solo tienes 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 u obtendrá un registro repetido. Esto se debe a que agregar o eliminar una fila significa que el desplazamiento de todas las filas después 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". luego, 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 paginar páginas una a una), puede leer este artículo sobre late row lookups para mejorar el rendimiento de LIMIT con una gran compensación.

+0

Esto se parece más a: P Si bien desapruebo por completo la implicación, que los 'nuevos' ids son siempre más grandes que los 'antiguos', * la mayor parte del tiempo * este será el caso, y entonces, creo, esto es bastante bueno'.De todos modos, sí, como demostraste, la paginación apropiada (sin degradación grave del rendimiento en grandes conjuntos de resultados) no es particularmente trivial y escribir 'limit 1000000, 10' y esperar que funcione no te llevará a ninguna parte. – shylent

+5

Tal paginación funciona solo hacia adelante – Aerse

+1

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

13

Este tutorial muestra una excelente manera de hacer paginación. Efficient Pagination Using MySQL

En resumen, evitar utilizar OFFSET o grande LIMIT

+15

¿Puede dar un resumen? – Andrew

+0

Ejemplo ..... ??? – Green

2

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

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

Consulta 1 funcione más rápido con los registros de pequeñas o medianas, si el número de registros igual a 5,000 o más, el resultado es similar.

Resultado para 500 registros:

Consulta1 tomar 9.9999904632568 milisegundos

Query2 tomar 19.999980926514 milisegundos

Resultado para 8.000 registros:

QUERY1 tomar 129.99987602234 milisegundos

Query2 tomar 160.00008583069 milisegundos

+0

Necesita poner un índice en 'id'. – Maarten

+4

¿Cómo es útil 'id> 0'? –

+1

Como dijo Maarten, esas dos consultas parecen fundamentalmente iguales, y probablemente se dividan en los mismos comandos a nivel de máquina de cualquier manera. Debe tener un problema de indexación o una versión realmente antigua de MySQL. – HoldOffHunger

10

Hay literatura al respecto:

El principal problema que ocurre con el uso de grandes OFFSET s . Evitan el uso de OFFSET con una variedad de técnicas, que van desde id selecciones de rango en la cláusula WHERE, hasta algún tipo de páginas de almacenamiento en caché o de precomputación.

No se sugieren soluciones en utilizar el índice, Lucas:

+0

getiing max id para cada consulta de paginación de consultas complejas resultaría en un uso no práctico, no productivo. ¡El rango, el número de fila y el tipo de paginación entre las paginas ayudan en el rendimiento! –

+0

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

+0

el enlace proporcionado solo parece cumplir con el pivote base pivote único, aplicación cruzada, CTE múltiple o mecánica de tablas derivadas? nuevamente estoy de acuerdo con mi caso con la reescritura de consultas de tal magnitud nuevamente para obtener maxid ¡es excesivo arquitectónico! y luego nuevamente permutación y combinación para n "número de columna con órdenes de clasificación! –

6

Definir OFFSET 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 utilizar 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 quiere decir compensación = 10 para la página-2? –

Cuestiones relacionadas