Skip to content

paginación con offset/Limit

El limit es la cantidad de registros a mostrar y el offset es la cantidad de resultados a saltarce, la formula para aplicar esto es:

((page - 1) * pagesize)

Las paginas deben empezar en 1.

Para lograr una paginación consistente, se debe ordenar por una columna única. De lo contrario, los resultados pueden ser inconsistentes. Si se necesita ordenar por una columna no única, también se debe agregar una columna única al orden. Así es como se puede implementar limit/offset con 2 columnas:

const getUsers = async (page = 1, pageSize = 3) => {
await db
.select()
.from(users)
.orderBy(asc(users.nombre), asc(users.id)) //nombre no es unique, pero el id si
.limit(pageSize)
.offset((page - 1) * pageSize);
}
await getUsers();

El problema de este tipo de paginación es que se debe escanear toda la tabla para saber que registros debe saltarce, por eso la paginación es más optima. Sin embargo, existe una forma de mejorar el rendimiento de este tipo de paginación que es implemnetarlo mediante deferred join.

La técnica de unión diferida es una solución de optimización que permite una paginación más eficiente. Realiza la paginación en un subconjunto de los datos en lugar de en toda la tabla. Este subconjunto se genera mediante una subconsulta, que se une con la tabla original más tarde. La técnica se denomina “diferida” porque la operación de unión se pospone hasta después de que se realiza la paginación.

Esto se saco de planetscale

La forma de como se aplico esto fue:

export const getProductsForUsers = async (page: number | 1, pageSize = 25) => {
const subQuery = db
.select({ id: ProductoTable.id })
.from(ProductoTable)
.orderBy(ProductoTable.nombre)
.limit(pageSize)
.offset((page - 1) * pageSize)
.as("query");
const products = await db
.select({
id: sql<string>`${ProductoTable.id}`,
nombre: sql<string>`${ProductoTable.nombre} `,
estadoActual: sql<string>`${estadoProducto.nombre}`,
precioMenor: sql<number>`${ProductoTable.precioMenor}`,
precioMayor: sql<number>`${ProductoTable.precioMayor}`,
stock: sum(Talla.cantidad).mapWith(Number),
descripcion: sql<string>`${ProductoTable.descripcion}`,
descuento: sql<number>`${ProductoTable.descuento}`,
fechaCreacion: sql<string>`${ProductoTable.fechaCreacion}`,
marca: sql<string>`${MarcaTable.nombre}`,
colores: sql<
string[]
>`json_group_array(distinct ${ColorProducto.nombreColor})`,
tallas: sql<
{ numeroTalla: number; cantidad: number; estadoActual: string }[]
>`json_group_array(json_object('numeroTalla', ${Talla.numeroTalla}, 'cantidad', ${Talla.cantidad}, 'estadoActual', ${estadoTallas.nombre}))`,
photos: sql<
{ url: string }[]
>`json_group_array(json_object('url', ${Images.url}))`,
categories: sql<
{ nombre: string; descripcion: string }[]
>`json_group_array(json_object('nombre', ${categoriaTable.nombre}, 'descripcion', ${categoriaTable.descripcion}))`,
})
.from(ProductoTable)
.innerJoin(subQuery, eq(ProductoTable.id, subQuery.id))
.leftJoin(MarcaTable, eq(ProductoTable.marcaId, MarcaTable.idMarca))
.leftJoin(
estadoProducto,
eq(ProductoTable.estadoActual, estadoProducto.idEstado),
)
.leftJoin(ColorProducto, eq(ProductoTable.id, ColorProducto.productoId))
.leftJoin(Talla, eq(ColorProducto.colorId, Talla.idTalla))
.leftJoin(estadoTallas, eq(Talla.estadoActual, estadoTallas.idEstado))
.leftJoin(Images, eq(ProductoTable.id, Images.productoId))
.innerJoin(Product_Catego, eq(ProductoTable.id, Product_Catego.productoId))
.innerJoin(
categoriaTable,
eq(Product_Catego.categoriaId, categoriaTable.id),
)
.orderBy(ProductoTable.nombre);
return products;
};