ROW_NUMBER() en SQL: qué es, cómo funciona y ejemplos prácticos

La función ROW_NUMBER() en SQL es una función de ventana que asigna un número de fila secuencial a cada registro dentro de un conjunto de resultados.
El contador comienza en 1 y se reinicia si se usa junto con PARTITION BY.

👉 Es ideal para generar rankings, numerar filas, obtener primeros registros por grupo o paginar resultados.

Sintaxis básica de ROW_NUMBER()

ROW_NUMBER() OVER (
  [PARTITION BY columna]
  ORDER BY columna
)

Elementos clave:

  • OVER: indica que es una función de ventana.
  • PARTITION BY: divide los resultados en grupos (opcional).
  • ORDER BY: define el orden en el que se asignan los números.

Ejemplo básico de ROW_NUMBER()

Tabla empleados:

idnombredepartamentosalario
1AnaVentas3000
2LuisVentas2500
3CarlaMarketing2800
4PedroMarketing3500

Consulta:

SELECT nombre, departamento, salario,
       ROW_NUMBER() OVER (ORDER BY salario DESC) AS posicion
FROM empleados;

Resultado:

nombredepartamentosalarioposicion
PedroMarketing35001
AnaVentas30002
CarlaMarketing28003
LuisVentas25004

👉 Numera las filas de mayor a menor salario.

ROW_NUMBER() con PARTITION BY

Podemos reiniciar la numeración por grupos.

SELECT nombre, departamento, salario,
       ROW_NUMBER() OVER (
         PARTITION BY departamento
         ORDER BY salario DESC
       ) AS ranking
FROM empleados;

Resultado:

nombredepartamentosalarioranking
AnaVentas30001
LuisVentas25002
PedroMarketing35001
CarlaMarketing28002

👉 Se reinicia la numeración en cada departamento.

Caso práctico: obtener el mejor empleado por departamento

Aquí ROW_NUMBER() se usa dentro de una subconsulta (ver más sobre SELECT y SUBQUERY):

SELECT nombre, departamento, salario
FROM (
  SELECT nombre, departamento, salario,
         ROW_NUMBER() OVER (
           PARTITION BY departamento
           ORDER BY salario DESC
         ) AS ranking
  FROM empleados
) sub
WHERE ranking = 1;

👉 Devuelve el empleado con mayor salario en cada departamento.

ROW_NUMBER() vs RANK() vs DENSE_RANK()

Aunque similares, hay diferencias:

  • ROW_NUMBER(): numera filas secuencialmente, sin importar empates.
  • RANK(): asigna el mismo número en caso de empate, pero salta posiciones.
  • DENSE_RANK(): asigna el mismo número en caso de empate, sin saltar posiciones.

Ejemplo:

SELECT nombre, salario,
       ROW_NUMBER() OVER (ORDER BY salario DESC) AS rn,
       RANK() OVER (ORDER BY salario DESC) AS rnk,
       DENSE_RANK() OVER (ORDER BY salario DESC) AS drnk
FROM empleados;

Resultado:

nombresalariornrnkdrnk
Pedro3500111
Ana3000222
Carla2800333
Luis2500444

Si Ana y Carla tuvieran el mismo salario:

  • ROW_NUMBER() seguiría numerando secuencialmente (2, 3).
  • RANK() asignaría (2, 2, 4).
  • DENSE_RANK() asignaría (2, 2, 3).

ROW_NUMBER() para paginación de resultados

Muy útil para mostrar resultados por páginas en aplicaciones:

SELECT *
FROM (
  SELECT id, nombre,
         ROW_NUMBER() OVER (ORDER BY id) AS fila
  FROM clientes
) sub
WHERE fila BETWEEN 11 AND 20;

👉 Devuelve los registros del 11 al 20.

Ejemplo con JOIN

ROW_NUMBER() también se puede combinar con JOIN para numerar registros relacionados, como pedidos de cada cliente.

SELECT c.nombre, p.total,
       ROW_NUMBER() OVER (
         PARTITION BY c.id
         ORDER BY p.fecha DESC
       ) AS orden_pedido
FROM clientes c
JOIN pedidos p ON c.id = p.cliente_id;

👉 Numera los pedidos de cada cliente en orden cronológico.

Errores comunes con ROW_NUMBER()

  1. Olvidar el ORDER BY dentro de OVER
    👉 Sin ORDER BY, el resultado no tiene un orden garantizado.
  2. Confundir con GROUP BY
    👉 ROW_NUMBER() no agrupa, conserva todas las filas.
  3. Usar ROW_NUMBER() sin subconsulta para filtrar
    👉 Para obtener “el primero de cada grupo”, hay que envolver en subconsulta.
  4. Esperar consistencia sin índice
    👉 El orden depende de la columna especificada y puede variar sin índices adecuados.

Buenas prácticas con ROW_NUMBER()

  • Siempre definir un ORDER BY claro en la función.
  • Usar PARTITION BY para rankings por grupos.
  • En paginación, ordenar por una columna única (ej. id).
  • Combinar con CTE (Common Table Expressions) para consultas más legibles.
  • Evitar usarlo sin contexto: no es un sustituto de LIMIT.

Casos de uso de ROW_NUMBER()

  1. Ranking de ventas por empleado.
  2. Primer/último registro por categoría.
  3. Detección de duplicados (filas con número > 1 se consideran repetidas).
  4. Paginar resultados en aplicaciones web.
  5. Análisis de series temporales (ordenando por fecha).

ROW_NUMBER() en distintos motores de bases de datos

  • PostgreSQL: soporte completo.
  • MySQL: desde versión 8 soporta funciones de ventana.
  • SQL Server: soporte completo, muy usado para paginación.
  • Oracle: disponible desde hace años.
  • SQLite: lo soporta desde versión 3.25.

👉 Hoy en día, prácticamente todos los motores modernos soportan ROW_NUMBER().

Preguntas frecuentes (FAQ)

1. ¿ROW_NUMBER() comienza en 0 o en 1?
Siempre comienza en 1.

2. ¿Se puede usar ROW_NUMBER() sin ORDER BY?
Sí, pero el resultado no es determinístico (puede cambiar entre ejecuciones).

3. ¿Cuál es la diferencia con RANK()?
ROW_NUMBER() nunca repite números, incluso en empates.

4. ¿Puedo usar ROW_NUMBER() con múltiples columnas en ORDER BY?
Sí, por ejemplo:

ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salario DESC, nombre ASC)

5. ¿Es costoso en rendimiento?
Depende del tamaño de la tabla y del ordenamiento. Conviene tener índices.

Conclusión

La función ROW_NUMBER() en SQL es fundamental para:

  • Numerar filas de manera secuencial.
  • Crear rankings dentro de grupos con PARTITION BY.
  • Paginar resultados en aplicaciones reales.
  • Resolver problemas de duplicados y obtener primeros registros.

Es una herramienta indispensable en análisis de datos y desarrollo de aplicaciones con bases de datos.

Aprende numeración de filas y orden de ventana en CASE y funciones de ventana y visita el Glosario SQL completo.

Scroll al inicio