Pregunta

Al paginar datos que provienen de una base de datos, necesita saber cuántas páginas habrá para representar los controles de salto de página.

Actualmente lo hago ejecutando la consulta dos veces, una vez envuelta en un count() para determinar los resultados totales, y una segunda vez con un límite aplicado para obtener solo los resultados que necesito para la página actual.

Esto parece ineficiente.¿Existe una mejor manera de determinar cuántos resultados se habrían devuelto antes? LIMIT ¿se aplicó?

Estoy usando PHP y Postgres.

¿Fue útil?

Solución

SQL puro

Las cosas han cambiado desde 2008.Puedes usar un función de ventana para obtener el recuento completo y el resultado limitado en una sola consulta.(Introducido con PostgreSQL 8.4 en 2009).

SELECT foo
     , count(*) OVER() AS full_count
FROM   bar
WHERE  <some condition>
ORDER  BY <some col>
LIMIT  <pagesize>
OFFSET <offset>

Tenga en cuenta que esto puede resultar considerablemente más caro que sin el recuento total.Se deben contar todas las filas, y es posible que un posible atajo que tome solo las filas superiores de un índice coincidente ya no sea útil.
No importa mucho con mesas pequeñas o full_count <= OFFSET + LIMIT.Asuntos para una sociedad sustancialmente mayor full_count.

Caso de esquina:cuando OFFSET es al menos tan grande como el número de filas de la consulta base, sin fila es regresado.Entonces tampoco obtienes full_count.Posible alternativa:

Considera el secuencia de eventos:

  1. WHERE cláusula (y JOIN condiciones, pero no aquí) filtran las filas calificadas de las tablas base.

    (GROUP BY y las funciones agregadas irían aquí.)

  2. Las funciones de ventana se aplican considerando todas las filas calificadas (dependiendo del OVER cláusula y la especificación del marco de la función).Lo simple count(*) OVER() se basa en todas las filas.

  3. ORDER BY

    (DISTINCT o DISTINCT ON iría aquí.)

  4. LIMIT / OFFSET se aplican según el orden establecido para seleccionar filas a devolver.

LIMIT / OFFSET se vuelve cada vez más ineficiente con un número creciente de filas en la tabla.Considere enfoques alternativos si necesita un mejor rendimiento:

Alternativas para obtener el conteo final

Existen enfoques completamente diferentes para obtener el recuento de filas afectadas (no el recuento completo antes OFFSET & LIMIT se aplicaron).Postgres tiene una contabilidad interna de cuántas filas se vieron afectadas por el último comando SQL.Algunos clientes pueden acceder a esa información o contar filas ellos mismos (como psql).

Por ejemplo, puede recuperar el número de filas afectadas en plpgsql inmediatamente después de ejecutar un comando SQL con:

GET DIAGNOSTICS integer_var = ROW_COUNT;

Detalles en el manual.

O puedes usar pg_num_rows en PHP.O funciones similares en otros clientes.

Relacionado:

Otros consejos

Como describo en mi blog , MySQL tiene una función llamada SQL_CALC_FOUND_ROWS . Esto elimina la necesidad de hacer la consulta dos veces, pero aún necesita hacer la consulta en su totalidad, incluso si la cláusula de límite le hubiera permitido detenerse antes.

Hasta donde yo sé, no hay una característica similar para PostgreSQL. Una cosa a tener en cuenta al hacer paginación (la cosa más común para la que se usa LIMIT en mi humilde opinión): hacer un & "; OFFSET 1000 LIMIT 10 &"; significa que el DB tiene que buscar al menos 1010 filas, incluso si solo le da 10. Una forma más eficaz de hacerlo es recordar el valor de la fila por la que está ordenando para la fila anterior ( el 1000 en este caso) y reescribe la consulta de esta manera: " ... WHERE order_row > value_of_1000_th LIMIT 10 " ;. La ventaja es que & Quot; order_row & Quot; probablemente esté indexado (si no, tienes un problema). La desventaja es que si se agregan nuevos elementos entre las vistas de la página, esto puede estar un poco fuera de sincronización (pero, de nuevo, puede que los visitantes no puedan observarlo y puede ser una gran ganancia de rendimiento).

Puede mitigar la penalización de rendimiento al no ejecutar la consulta COUNT () cada vez. Caché el número de páginas para, digamos 5 minutos antes de que la consulta se ejecute nuevamente. A menos que vea una gran cantidad de INSERT, eso debería funcionar bien.

Dado que Postgres ya realiza una cierta cantidad de almacenamiento en caché, este tipo de método no es tan ineficiente como parece. Definitivamente no se duplica el tiempo de ejecución. Tenemos temporizadores integrados en nuestra capa de base de datos, así que he visto la evidencia.

Teniendo en cuenta que necesita saber con el propósito de paginación, sugeriría ejecutar la consulta completa una vez, escribir los datos en el disco como caché del lado del servidor, y luego alimentar eso a través de su mecanismo de paginación.

Si está ejecutando la consulta COUNT con el fin de decidir si proporcionar los datos al usuario o no (es decir, si hay > registros X, devolver un error), debe seguir con el enfoque COUNT.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top