¿Cómo funciona la paginación en SQL con limit y offset?
La paginación es una técnica esencial cuando trabajamos con bases de datos, especialmente cuando tratamos con conjuntos de datos grandes. La capacidad de paginar de manera eficiente permite que aplicaciones, tanto web como móviles, ofrezcan una experiencia de usuario más fluida y ágil. El uso de limit y offset en SQL ofrece dicha flexibilidad al permitir que el cliente decida cuántos elementos recuperará de la base de datos y desde qué punto comenzar.
¿Qué es limit?
El limit nos permite especificar el número de elementos que deseamos obtener de la base de datos. Por ejemplo:
Una aplicación móvil puede optar por mostrar 5 elementos a la vez debido a sus restricciones de pantalla.
En contraste, una aplicación web puede mostrar 10 elementos por vez, aprovechando el espacio adicional de la interfaz.
Puedo decidir, mediante el limit, cuántos elementos quiero por cada página de manera flexible y adaptativa a las necesidades del cliente.
¿Cuál es el propósito de offset?
El offset actúa como un apuntador que indica el inicio de nuestra selección en el conjunto de resultados. Si lo consideramos como un array, el offset comenzaría en cero y determina cuántos elementos queremos "saltar" antes de empezar a listar.
Por ejemplo:
Si queremos los primeros dos elementos, establecemos offset en 0 y limit en 2.
Para obtener los elementos 3 y 4, mantenemos el limit en 2 y cambiamos el offset a 2.
Esto permite navegar por las páginas siguientes simplemente ajustando el valor de offset.
¿Cómo se implementa la paginación en una base de datos?
La mayoría de las bases de datos, incluyendo SQLite, soportan nativamente los elementos offset y limit. Aquí hay un ejemplo sobre cómo podrías implementar la paginación a través de código:
SELECT*FROM productos
LIMIT10OFFSET20;
Este ejemplo seleccionaría 10 productos comenzando desde el número 21 en la base de datos (ya que el offset empieza en cero).
En MongoDB y otras tecnologías, la lógica es similar, aunque las nomenclaturas pueden variar; por ejemplo, "skip" se utiliza en lugar de "offset".
¿Cómo integrar paginación dinámica mediante parámetros tipo query?
En el contexto de aplicaciones backend, la paginación se puede manejar a través de parámetros tipo query. Esto permite que la paginación sea opcional según las necesidades del usuario:
const express =require('express');const app =express();app.get('/productos',async(req, res)=>{const limit =parseInt(req.query.limit)||10;const offset =parseInt(req.query.offset)||0;// Consultar a la base de datosconst products =awaitgetProductsFromDatabase(limit, offset); res.json(products);});
Proceso paso a paso:
Definir esquema y parámetros: Configurar los parámetros limit y offset en el esquema de productos.
Validación de parámetros: Comprobar que los valores recibidos son numéricos y lógicos antes de procesar la consulta.
Opcionalidad de la paginación: Si no se reciben parámetros, la aplicación debe retornar todos los productos.
Consulta dinámica: Ajustar la consulta basada en los parámetros para decidir si incluir limit u offset.
Esta técnica no solo es altamente flexible, sino que también se adapta a diferentes escenarios de aplicación al tratarse de una funcionalidad básica y universal. Además, permite escalabilidad y mejor rendimiento en operaciones con bases de datos grandes al evitar la carga innecesaria de datos.
Ejemplo práctico de paginación
Supongamos que tenemos diez productos y queremos aplicar paginación de dos en dos:
Primera página: limit = 2, offset = 0 devolverá productos 1 y 2.
Segunda página: limit = 2, offset = 2 devolverá productos 3 y 4.
Es importante contar con un número suficiente de productos en la base de datos para una paginación efectiva. Esta técnica garantiza que la información se entregue de manera eficiente y controlada.
¡Implementa estas estrategias y transforma la experiencia del usuario al siguiente nivel, asegurando además una mayor eficiencia en el manejo de datos!
Limit → Número de elementos que deseo traer. Límite de elementos que deseo traer en cada página.
Offset → Apuntador, es decir, cuántos elementos quiero escapar.
Ejemplo: Si en una página tengo una lista de elementos [1, 2, 4, 5, 6]. Si limit = 2 y offset = 0, el resultado será [1, 2].
Si deseo que en la siguiente página siga trayendo 2 elementos, entonces limit = 2 y offset = 2, solo cambia el apuntador. El resultado será [3, 4].
Si deseo que en la siguiente página siga trayendo 2 elementos, entonces limit = 2 y offset = 4. El resultado será [5, 6].
Se puede implementar limit y offset de forma nativa, en este caso lo hacemos en el método find() del product.service.js. Cabe mencionar que usualmente vienen por parámetros tipo query y la mayoría de las veces son opcionales.
Debido a que son dinámicos, es decir, si no se especifica limit y offset entonces trae todos los productos de la BD, en caso contrario se haría la paginación.
Se hace una validación para que sea de forma dinámica de la siguiente manera. Las options son por defecto (incluye ‘category’), pero si envían un limit y offset entonces se agregan a options.
Holaa!, muchas gracias por tus apuntes. Una pequeña corrección en el archivo product.route.js en la función findAll se recibe los parametros por query no por params
Yo cambié un poco mi código, para que:
.
Por defecto el valor de limit sea 2
El offset empiece por defecto en 0
De esta forma si se hace un GET a la ruta se mostrarán solo los dos primeros items
Además se podría paginar unicamente enviando el query offset mientras se mantiene el limit por defecto, o viceversa.
Por fin entendí que es el offset, cuantos elementos quiero escapar de la consulta
mmmm yes
En mi caso no entiendo porque cuando ingresaba el limit y el offset me los traía como texto y no funcionaba por lo que me toco que parsearlos a integer.
Servicio
// Retorna los productos totales o bien por limiteasyncgetAll(query){const options ={attributes:['id','name','description','image','price'],include:[{association:'measure_unit',attributes:['description',]},{association:'category',attributes:['description',]},]}const{limit, offset}= query;console.log(typeof(limit))console.log(typeof(offset))if(limit && offset){ options.limit=parseInt(limit); options.offset=parseInt(offset);}const rta =await models.Product.findAll(options)return rta;}
recuerden especificar la versión cuando usen faker: en este caso la ultima versión operativa fue faker@5.5.3, antes que sea deprecada por la comunidad.
Esta librería la usamos en el curso pasado jeje
Hice algunos cambios para que sea más entendible para enviar la página y calcule el offset de forma automática.
EL profe Nico es muy crack. Me parecía dificil meterme con la parte del back, con docker, postgre y todo eso pero lo explica tan bien y tan facil que me impulsa a seguir aprendiendo y enamorarme de la programación!!
Una pequeña observacion es que, la condicional de (limit && offset) funciona porque son parametros que vienen de la ++url++, es decir son valores string.
Si los parseara a number, no funcionaria cuando el offset sea 0 porque es un valor falsy. Por lo que esa validacion esta un poquito debil.
¿Como podría ser mejor?
So... no los parseemos a Number entonces ¯\_(ツ)_/¯
Esta validacion de Joi se asegura de que ambas propiedades del query se encuentran presentes pero no falla cuando no existe ninguna (comportamiento similar al operador logico xnor):
Que hermosa firma de realizar una paginación. Siempre fue una pesadilla para mí realizar este feature.
Pregunta no relacionada; con limit y offset es con lo que PHP lleva siglos haciendo paginación ¿Verdad?
Hola Andres,
La paginación se hace con limit y offset, el tema es más de la base de datos, y los lenguajes en sus ORM's la implementan.
Tengo una tragedia, vengo siguiendo el curso hasta ahora clase 23 y cuando enciendo el compu para continuar trabajando me doy cuenta que no tengo ninguna tabla nada se guardó nada en postgres y pg, cómo eso puede ser posible?? estoy que lloró!!! le hice una migrations:run y me trajo todas las tablas pero sin sus actualizaciones, es decir, sin sin las asociaciones, sólo los nombres no entiendo qué puedo hacer, si mi código tengo todo porque en postgres nada???
Hola Evelyn ¡
Lo que te recomiendo es que generes una nueva migración y en esta corras todas las tablas que tenemos hasta ahora, desde que no se haya borrado el código todo se puede volver a recuperar
para quienes lo hacemos con postman solo es ponser el limit y el offset en la url, no hay una ventana de Query como en insomnia
Hola, en Postman si tienen el apartado query, está justo en params abajo del lugar donde pones el enlace-
offset 4 = salteme los primeros 4 elementos,
limit 2 = traigame solo dos elementos
Esto de la páginación esta muy pro! y entonces en front cada página, hace un fetch con cada params?
Si no me equivoco si, en cada fetch se va incrementando el offset en el front
¿Qué efectos tiene en la paginación si entre peticiones los datos son modificados? Es decir, se eliminan o se agregan registros.
La frontend tendría una trama de datos, esos datos se quedarían ahí sin más; se vería la información antes de ser modificada, hasta que se recargue la página y se vuelva a traer esa trama de datos
Si no me aparece en insomnia la pestaña de query como hago ?
¿Cómo puedo aplicar limit y offset para consultar la información de una tabla pero ordenada por un campo (Alfabéticamente de Z - A) ?
Explicación:
Hola, la función findAndCountAll de sequelize recibe en sus parámetros un atributo llamado order. Pude ser un string, con la columna a ordenar; o un array de arrays, con las columnas y la forma de ordenamiento (asc o desc).