funciones-ventana-row-number-rank

Funciones de ventana ROW_NUMBER, RANK y DENSE_RANK

  • 5 min

Las funciones de ventana permiten asignar números secuenciales o posiciones de ranking a las filas de una consulta sin colapsar los resultados originales.

Hasta ahora, cuando queríamos calcular agregados (como sumas o conteos), teníamos que usar GROUP BY. Pero eso tenía un precio: perdíamos el detalle de las filas individuales. Colapsábamos todo en un resumen.

¿Qué pasa si quiero saber el “Top 3 de ventas” pero sin perder la información de quién hizo la venta y cuándo? ¿O si quiero numerar las filas de una consulta?

Aquí entran en juego las funciones de ventana. En este artículo nos centraremos en las funciones de Ranking: ROW_NUMBER, RANK y DENSE_RANK.

¿Qué es una función de ventana?

Una función de ventana realiza un cálculo a través de un conjunto de filas que están relacionadas con la fila actual.

A diferencia del GROUP BY, no agrupa las filas en una sola. Las filas mantienen su identidad, pero se les añade una columna extra con el resultado del cálculo.

La cláusula OVER

La cláusula OVER define una ventana de datos sobre la cual se aplicará una función de agregación. Esta ventana puede ser toda la tabla, un subconjunto de filas o incluso filas relacionadas con la fila actual.

La sintaxis básica es la siguiente:

FUNCION_AGREGACION() OVER ( [PARTITION BY columna] [ORDER BY columna] )
Copied!
  • FUNCION_AGREGACION: Puede ser cualquier función de agregación, como SUM, AVG, COUNT, etc.
  • PARTITION BY: Divide los datos en grupos (particiones) basados en una o más columnas.
  • ORDER BY: Ordena las filas dentro de cada partición.

Los tres compañeros de OVER

Imaginad que hemos hecho un examen en clase y tenemos las siguientes notas. Vamos a ver cómo puntúa cada función.

VendedorImporte
Luis1000€
Ana800€
Pedro800€
Sofía500€

Fíjate que Ana y Pedro han empatado con 800€. Aquí es donde surgen las diferencias.

Como su nombre indica, asigna un número de fila secuencial y único.

  • Simplemente cuenta: 1, 2, 3, 4…
  • No le importan los empates. Si dos filas tienen el mismo valor en el ORDER BY, el motor decidirá arbitrariamente quién va antes (a menos que añadas más criterios de desempate).

Uso principal: Paginación y eliminar duplicados.

Calcula el ranking al estilo “competición olímpica”.

  • Si hay un empate, ambos reciben la misma posición.
  • Deja huecos en la numeración posterior.

En nuestro ejemplo: Ana (1), Beto (2), Carla (2)… Dani será el 4. (Se salta el 3 porque hay dos personas delante de él empatadas).

Calcula el ranking sin dejar huecos.

  • Si hay empate, ambos reciben la misma posición.
  • NO deja huecos. La siguiente posición es el número inmediato.

En nuestro ejemplo: Ana (1), Beto (2), Carla (2)… Dani será el 3.

Ejemplo en código

Vamos a verlo con una consulta real sobre una tabla de Ventas.

SELECT 
    Vendedor,
    Importe,
    ROW_NUMBER() OVER (ORDER BY Importe DESC) as Fila,
    RANK()       OVER (ORDER BY Importe DESC) as Ranking,
    DENSE_RANK() OVER (ORDER BY Importe DESC) as RankingDenso
FROM Ventas;
Copied!

Resultado:

VendedorImporteFila (Row_Number)Ranking (Rank)RankingDenso
Luis1000€111
Ana800€222
Pedro800€322
Sofía500€443
  • Fila: Pedro es el 3 porque sí (o porque alfabéticamente va después de Ana, pero es secuencial).
  • Rank: Pedro empata con Ana (2). Sofía salta al 4 (se pierde el 3).
  • Dense: Pedro empata con Ana (2). Sofía es la 3 (no se pierden números).

La potencia de PARTITION BY

La cláusula PARTITION BY es opcional y se utiliza dentro de OVER para dividir los datos en grupos más pequeños, llamados particiones.

Cada partición se trata como un conjunto independiente de datos, y la función de agregación se aplica a cada uno de estos grupos.

Esto nos permite reiniciar el ranking para cada grupo de datos.

Por ejemplo, queremos saber quiénes son los 2 mejores vendedores de cada departamento.

SELECT * FROM (
    SELECT 
        Departamento,
        Vendedor,
        Ventas,
        DENSE_RANK() OVER (
            PARTITION BY Departamento -- Reinicia el contador al cambiar de Dpto
            ORDER BY Ventas DESC
        ) as Posicion
    FROM Empleados
) TablaDerivada
WHERE Posicion <= 2;
Copied!
  1. SQL ordena las ventas dentro del departamento de ‘Ventas’. Asigna 1, 2, 3…
  2. Cuando llega al departamento de ‘IT’, el contador vuelve a 1.
  3. Finalmente, filtramos por Posicion <= 2 para obtener el podio de cada área.

Esta técnica es mucho más eficiente y legible que usar subconsultas correlacionadas complejas para buscar “el máximo por grupo”.