Top-N por grupo
IntermedioPara quedarte con los N primeros de cada grupo (los dos productos más vendidos por categoría, los tres artistas por país), numeras las filas con una función de ranking dentro de cada grupo (PARTITION BY) en una subconsulta o CTE, y filtras ese número afuera, en la consulta siguiente.
Sintaxis
WITH rankeadas AS (
SELECT grupo, elemento, metrica,
rank() OVER (PARTITION BY grupo ORDER BY metrica DESC) AS puesto
FROM tabla
)
SELECT grupo, puesto, elemento, metrica
FROM rankeadas
WHERE puesto <= 2;Ejemplo
WITH por_cocina AS (
SELECT ci.name AS ciudad, r.cuisine AS cocina, count(*) AS pedidos
FROM orders AS o
INNER JOIN restaurants AS r ON r.id = o.restaurant_id
INNER JOIN cities AS ci ON ci.id = r.city_id
WHERE o.status = 'delivered'
AND o.placed_at >= '2025-08-01'
AND o.placed_at < '2025-09-01'
AND ci.name IN ('Bogotá', 'Buenos Aires', 'Córdoba')
GROUP BY ci.name, r.cuisine
), rankeadas AS (
SELECT ciudad, cocina, pedidos,
rank() OVER (PARTITION BY ciudad ORDER BY pedidos DESC) AS puesto
FROM por_cocina
)
SELECT ciudad, puesto, cocina, pedidos
FROM rankeadas
WHERE puesto <= 2
ORDER BY ciudad, puesto, cocina;Resultado
| ciudad | puesto | cocina | pedidos |
|---|---|---|---|
| Bogotá | 1 | parrilla | 25 |
| Bogotá | 2 | china | 20 |
| Bogotá | 2 | helados | 20 |
| Buenos Aires | 1 | pizza | 40 |
| Buenos Aires | 2 | china | 39 |
| Córdoba | 1 | cafetería | 24 |
| Córdoba | 2 | empanadas | 18 |
Cómo leerlo
Son los dos tipos de cocina con más pedidos entregados en agosto de 2025 en cada ciudad. Bogotá devuelve tres filas porque china y helados empatan en 20 y rank() les da el mismo puesto. Con row_number() saldría solo una de las dos, elegida sin criterio si no agregas un desempate en el ORDER BY. Antes de escribir la consulta, conviene preguntar qué hacer con los empates.
Error común
Así no
SELECT ci.name AS ciudad, r.cuisine AS cocina, count(*) AS pedidos
FROM orders AS o
INNER JOIN restaurants AS r ON r.id = o.restaurant_id
INNER JOIN cities AS ci ON ci.id = r.city_id
WHERE o.status = 'delivered'
GROUP BY ci.name, r.cuisine
ORDER BY pedidos DESC
LIMIT 2;Usar LIMIT para un top por grupo. LIMIT corta el resultado completo, no cada grupo: devuelve las dos combinaciones con más pedidos de todas las ciudades juntas. Tampoco sirve filtrar el ranking en el WHERE de la misma consulta, porque las funciones de ventana se calculan después del WHERE; por eso el filtro va en la consulta de afuera.
En otros motores: Snowflake, BigQuery, PostgreSQL, SQL Server
SnowflakeBigQuery
QUALIFYfiltra por el resultado de una función de ventana sin necesidad de una subconsulta. PostgreSQL, MySQL y SQL Server no lo tienen.SELECT ciudad, cocina, pedidos FROM por_cocina QUALIFY rank() OVER (PARTITION BY ciudad ORDER BY pedidos DESC) <= 2;PostgreSQLSQL Server
Otra forma es recorrer los grupos y traer los N primeros de cada uno con una subconsulta correlacionada:
CROSS JOIN LATERAL (… LIMIT 2)en PostgreSQL yCROSS APPLY (SELECT TOP (2) …)en SQL Server.
Estos fragmentos no se ejecutan en el curso: se muestran como referencia de cada motor.