Seleccionar los N primeros
Seleccionar los N registros principales de una tabla es una operación frecuente: los 10 productos más vendidos, los 5 clientes con mayor gasto, las 3 categorías con más artículos. MySQL ofrece varias formas de hacerlo, desde el simple LIMIT hasta técnicas avanzadas con funciones de ventana para manejar empates y agrupaciones.
LIMIT básico
La forma más directa es ORDER BY combinado con LIMIT:
SELECT nombre, precio
FROM productos
ORDER BY precio DESC
LIMIT 5;| nombre | precio |
|---|---|
| ASUS ROG Zephyrus | 1899.99 |
| Lenovo ThinkPad X1 | 1549.00 |
| MacBook Air M3 | 1399.00 |
| iPhone 15 Pro | 1299.99 |
| Samsung Galaxy S24 | 899.99 |
LIMIT 5 devuelve las primeras 5 filas después de aplicar el ordenamiento. Sin ORDER BY, el resultado no tiene un orden determinista y las filas seleccionadas serían impredecibles.
Los N primeros por grupo
Un escenario más interesante es obtener los N primeros de cada grupo. Por ejemplo, los 3 productos más caros de cada categoría. Aquí es donde ROW_NUMBER() resulta esencial:
SELECT nombre, categoria_nombre, precio
FROM (
SELECT
p.nombre,
c.nombre AS categoria_nombre,
p.precio,
ROW_NUMBER() OVER (
PARTITION BY p.categoria_id
ORDER BY p.precio DESC
) AS rn
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
) ranked
WHERE rn <= 3;| nombre | categoria_nombre | precio |
|---|---|---|
| iPhone 15 Pro | Smartphones | 1299.99 |
| Samsung Galaxy S24 | Smartphones | 899.99 |
| Google Pixel 8 | Smartphones | 699.00 |
| ASUS ROG Zephyrus | Portátiles | 1899.99 |
| Lenovo ThinkPad X1 | Portátiles | 1549.00 |
| MacBook Air M3 | Portátiles | 1399.00 |
PARTITION BY categoria_id reinicia la numeración para cada categoría, y ORDER BY precio DESC asegura que los más caros tengan los números más bajos. Filtrando rn <= 3, obtenemos exactamente los 3 primeros de cada grupo.
Manejar empates con RANK y DENSE_RANK
ROW_NUMBER() asigna un número único a cada fila, incluso si hay empates. Si quieres incluir todos los registros empatados en el N-ésimo puesto, usa RANK() o DENSE_RANK():
SELECT nombre, precio, ranking
FROM (
SELECT
nombre,
precio,
DENSE_RANK() OVER (ORDER BY precio DESC) AS ranking
FROM productos
) ranked
WHERE ranking <= 3;| nombre | precio | ranking |
|---|---|---|
| ASUS ROG Zephyrus | 1899.99 | 1 |
| Lenovo ThinkPad X1 | 1549.00 | 2 |
| MacBook Air M3 | 1399.00 | 3 |
Si hubiera dos productos con precio 1399.00, DENSE_RANK() les asignaría a ambos el rango 3 y los incluiría en el resultado. Con ROW_NUMBER(), uno de ellos tendría rango 4 y quedaría fuera.
La diferencia entre RANK() y DENSE_RANK() es cómo manejan los saltos: RANK() salta números (1, 2, 2, 4), mientras que DENSE_RANK() no salta (1, 2, 2, 3).
Top N con porcentaje
Para seleccionar un porcentaje de las filas en lugar de un número fijo:
SET @total = (SELECT COUNT(*) FROM productos);
SELECT nombre, precio
FROM productos
ORDER BY precio DESC
LIMIT FLOOR(@total * 0.10);Esto selecciona el 10% de productos con mayor precio. Nota que LIMIT no acepta expresiones directamente en versiones anteriores a MySQL 8.0, por lo que la variable es necesaria.
Los N primeros clientes por gasto
Un ejemplo práctico de los top N con agregación:
SELECT
c.nombre,
c.apellidos,
SUM(p.total) AS gasto_total,
COUNT(p.id) AS num_pedidos
FROM clientes c
JOIN pedidos p ON c.id = p.cliente_id
GROUP BY c.id, c.nombre, c.apellidos
ORDER BY gasto_total DESC
LIMIT 10;| nombre | apellidos | gasto_total | num_pedidos |
|---|---|---|---|
| Elena | Sánchez Ruiz | 15890.50 | 23 |
| Pablo | Martín Gómez | 12340.00 | 18 |
| Laura | Fernández Díaz | 11250.75 | 15 |
LIMIT con OFFSET para paginación
LIMIT acepta un segundo argumento para desplazar el inicio:
-- Primeros 10
SELECT nombre, precio FROM productos ORDER BY precio DESC LIMIT 10;
-- Del 11 al 20
SELECT nombre, precio FROM productos ORDER BY precio DESC LIMIT 10 OFFSET 10;
-- Del 21 al 30
SELECT nombre, precio FROM productos ORDER BY precio DESC LIMIT 10 OFFSET 20;Esto es la base de la paginación. Sin embargo, para offsets grandes (LIMIT 10 OFFSET 100000), el rendimiento se degrada porque MySQL debe leer y descartar las primeras 100,000 filas. Para paginación eficiente en tablas grandes, usa la técnica de cursor basada en el último valor visto.
Rendimiento
Para obtener los N primeros, un índice en la columna de ordenamiento permite a MySQL leer solo N filas del índice sin ordenar toda la tabla:
CREATE INDEX idx_precio ON productos(precio DESC);
-- Esta consulta usa el índice directamente, sin filesort
SELECT nombre, precio FROM productos ORDER BY precio DESC LIMIT 10;Sin el índice, MySQL necesita leer toda la tabla y ordenarla en memoria o en disco antes de devolver las 10 primeras filas. Con el índice, lee directamente las 10 primeras entradas del índice.
Seleccionar los N primeros registros es una de las operaciones más comunes en SQL. LIMIT cubre los casos simples, y las funciones de ventana ROW_NUMBER(), RANK() y DENSE_RANK() resuelven los escenarios más avanzados con agrupaciones y empates.
Practica con los N primeros
Usa el editor para obtener los registros con valores más altos:
Escrito por Eduardo Lázaro
