Filas superiores por grupo con ROW_NUMBER
Aplique el patrón clásico de particionar y clasificar para obtener las 3 filas superiores de cada categoría
Filas superiores por grupo con ROW_NUMBER es una lección gratuita de Coding Interview Prep en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Coding Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Coding Interview Prep incluye 4 lecciones en total.
La cuestión del top-N por grupo
Una de las preguntas más habituales en las entrevistas de SQL parece sencilla: «Devuelva los 3 empleados con el salario más alto de cada departamento». Los candidatos que recurren inmediatamente a LIMIT fallan, porque LIMIT limita todo el conjunto de resultados, no cada grupo.
El entrevistador está comprobando si conoce las funciones de ventana. La respuesta canónica es: numere las filas dentro de cada grupo y, después, conserve las filas cuyo número sea ≤ N. En esta lección se desarrolla ese patrón paso a paso.
Por qué LIMIT no puede resolverlo
Suponga que escribe la consulta siguiente. Devuelve solo 3 filas en total de toda la tabla, no 3 por departamento.
LIMIT (o TOP o FETCH FIRST) opera sobre el conjunto de resultados final. En SQL estándar no existe un LIMIT por grupo. Cuando un entrevistador le oye sugerir LIMIT 3 para un problema por grupo, eso indica que aún no ha interiorizado el particionado.
-- WRONG: only 3 rows total, not 3 per department
SELECT department, name, salary
FROM employees
ORDER BY salary DESC
LIMIT 3;Conozca ROW_NUMBER
ROW_NUMBER() es una función de ventana que asigna un entero único y sin saltos a cada fila según un orden. Por sí sola, numera todo el resultado.
El ingrediente clave es PARTITION BY: reinicia la numeración en 1 para cada grupo. Combine PARTITION BY department con ORDER BY salary DESC y cada departamento tendrá su propia clasificación 1, 2, 3, ... por salario.
SELECT
name,
department,
salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS rn
FROM employees;Cómo leer el resultado numerado
Después de ejecutar la consulta anterior, cada fila contiene un valor rn. Dentro de cada departamento, el salario más alto recibe rn = 1, el siguiente recibe 2, y así sucesivamente. Al comenzar un departamento nuevo, la numeración vuelve a 1.
- Ventas: Ana (1), Bo (2), Cal (3), Dee (4)
- Ingeniería: Eve (1), Fin (2), Gus (3)
Ahora, «top 3 por departamento» simplemente significa «conservar las filas donde rn <= 3».
No puede filtrar rn en WHERE
El paso siguiente más natural es WHERE rn <= 3, pero falla. Las funciones de ventana se calculan después de la cláusula WHERE según el orden lógico de ejecución, por lo que el alias rn todavía no existe cuando se ejecuta WHERE.
A los entrevistadores les encanta este error. La solución consiste en calcular la función de ventana en una subconsulta o CTE y, después, filtrar el resultado de esa consulta interna en una consulta externa.
-- ERROR: rn does not exist in WHERE
SELECT name, department, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
WHERE rn <= 3;La solución canónica con un CTE
Incluya la numeración en un CTE llamado ranked y, después, seleccione sus datos aplicando el filtro en el WHERE externo. Esta es la respuesta que los entrevistadores esperan ver y se lee con claridad.
Memorice esta estructura: particione por el grupo, ordene por la métrica y filtre rn ≤ N en la consulta externa. Se generaliza a top-1, top-5 o cualquier N cambiando un solo número.
WITH ranked AS (
SELECT
name, department, salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS rn
FROM employees
)
SELECT name, department, salary
FROM ranked
WHERE rn <= 3
ORDER BY department, rn;La forma con una subconsulta
Si el dialecto del entrevistador es antiguo o prefiere las subconsultas, puede aplicar la misma lógica dentro de una tabla derivada en FROM. Recuerde que una tabla derivada debe tener un alias (r en este caso); de lo contrario, se producirá un error de sintaxis.
Las formas con CTE y con tabla derivada son intercambiables para este problema. Elija la que el entrevistador considere más legible; ambas son igualmente correctas.
SELECT name, department, salary
FROM (
SELECT name, department, salary,
ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rn
FROM employees
) AS r
WHERE rn <= 3;Top-1: el mejor de cada grupo
«Encuentre el empleado con el salario más alto de cada departamento» es simplemente N = 1. Establezca el filtro en rn = 1.
¿Por qué no usar MAX(salary) con GROUP BY department? Porque MAX le proporciona el valor del salario, pero no el resto de la fila de ese empleado (su nombre, fecha de contratación, etc.). ROW_NUMBER conserva intacta toda la fila ganadora, que es lo que normalmente pide realmente la pregunta.
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rn
FROM employees
)
SELECT name, department, salary, hire_date
FROM ranked
WHERE rn = 1;Cómo añadir un desempate determinista
ROW_NUMBER siempre devuelve exactamente N filas, incluso cuando hay salarios empatados. Sin embargo, qué fila empatada recibe rn = 1 es arbitrario si no resuelve el empate. Si dos personas ganan 90000 y solo conserva rn = 1, la fila elegida puede variar entre ejecuciones.
Añada una clave de ordenación secundaria y única, como employee_id, para que el resultado sea estable y reproducible. Los entrevistadores valoran que los candidatos mencionen la determinación de resultados sin que se les pida.
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC, employee_id ASC
) AS rnUn ejemplo práctico completo
Dada una tabla sales con region, product y revenue, devuelva los 2 productos con mayores ingresos por región. La receta es la misma: particione por region, ordene por revenue DESC y conserve rn <= 2.
Observe que solo cambian la columna de partición y la columna de la métrica. La estructura es idéntica independientemente del dominio empresarial.
WITH ranked AS (
SELECT region, product, revenue,
ROW_NUMBER() OVER (
PARTITION BY region ORDER BY revenue DESC, product
) AS rn
FROM sales
)
SELECT region, product, revenue
FROM ranked
WHERE rn <= 2
ORDER BY region, rn;Rendimiento y aspectos clave para comentar
Para demostrar conocimientos más allá de la corrección, mencione lo siguiente:
- Un índice sobre
(department, salary DESC)ayuda al motor a producir filas ordenadas por partición de forma eficiente. - El enfoque con funciones de ventana recorre la tabla una sola vez, lo que es mucho mejor que una subconsulta correlacionada que se ejecuta para cada fila.
- Para casos de top-N-of-1 muy grandes, algunos motores admiten
DISTINCT ON(Postgres) como atajo, peroROW_NUMBERes el estándar portable.
Indique siempre su criterio de desempate y confirme el valor solicitado de N.
Comprobación rápida
Ponga a prueba su comprensión del patrón top-N por grupo.
Resumen: top-N por grupo
El patrón en una frase: particione por el grupo, ordene por la métrica, asigne ROW_NUMBER y, después, conserve rn ≤ N en una consulta externa.
LIMITlimita todo el conjunto, nunca cada grupo.- No puede filtrar el alias de una función de ventana en
WHERE; inclúyalo en un CTE o una subconsulta. - Añada un criterio de desempate único para obtener resultados deterministas.
- Top-1 conserva toda la fila ganadora, a diferencia de
MAX+GROUP BY.
Cambie un solo número y la misma consulta resolverá top-1, top-5 o cualquier N.
Preguntas frecuentes
¿La lección «Filas superiores por grupo con ROW_NUMBER» es gratis?
Sí — el texto completo de «Filas superiores por grupo con ROW_NUMBER» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Coding Interview Prep, actualiza a CoddyKit PRO. El curso de Coding Interview Prep incluye 4 lecciones en total.
¿Qué aprenderé en «Filas superiores por grupo con ROW_NUMBER»?
Aplique el patrón clásico de particionar y clasificar para obtener las 3 filas superiores de cada categoría Practicas Coding Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Coding Interview Prep?
No se requiere experiencia previa. Coding Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Filas superiores por grupo con ROW_NUMBER»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Coding Interview Prep?
Sí. Cada lección de Coding Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Filas superiores por grupo con ROW_NUMBER
- Gestionar empates en los valores superiores
- Eliminar duplicados de forma segura
- Conservar la última fila por clave