ROW_NUMBER, RANK y DENSE_RANK: numerar y clasificar filas

Ordenar un resultado no asigna una posición que podamos reutilizar. Si necesitamos elegir el pedido más reciente de cada cliente, numerar resultados o representar empates, utilizamos funciones de clasificación.

Las tres funciones trabajan con OVER y requieren un orden. La diferencia aparece cuando varias filas comparten la misma posición lógica.

La idea

  • ROW_NUMBER asigna un número distinto a cada fila.
  • RANK asigna la misma posición a los empates y deja huecos después.
  • DENSE_RANK asigna la misma posición a los empates sin dejar huecos.

Sintaxis

ROW_NUMBER() OVER
(
    PARTITION BY ColumnaDeGrupo
    ORDER BY ColumnaDeOrden
)

Podemos sustituir ROW_NUMBER por RANK o DENSE_RANK según la forma en que deban tratarse los empates.

Ejemplo: pedido más reciente de cada cliente

;WITH PedidosNumerados AS
(
    SELECT
        p.IdPedido,
        p.IdCliente,
        p.FechaPedido,
        p.Estado,
        ROW_NUMBER() OVER
        (
            PARTITION BY p.IdCliente
            ORDER BY p.FechaPedido DESC, p.IdPedido DESC
        ) AS Numero
    FROM dbo.Pedido AS p
)
SELECT
    IdPedido,
    IdCliente,
    FechaPedido,
    Estado
FROM PedidosNumerados
WHERE Numero = 1;

Cada cliente inicia su propia numeración. El orden descendente coloca primero el pedido más reciente y IdPedido resuelve un posible empate de fechas.

Comparar el tratamiento de empates

SELECT
    p.Nombre,
    p.Precio,
    ROW_NUMBER() OVER (ORDER BY p.Precio DESC) AS Numero,
    RANK()       OVER (ORDER BY p.Precio DESC) AS Posicion,
    DENSE_RANK() OVER (ORDER BY p.Precio DESC) AS PosicionDensa
FROM dbo.Producto AS p;

Si los precios ordenados fueran 800, 800 y 600, obtendríamos:

Precio ROW_NUMBER RANK DENSE_RANK
800 1 1 1
800 2 1 1
600 3 3 2

ROW_NUMBER separa físicamente las filas empatadas. RANK salta la posición 2. DENSE_RANK continúa sin huecos.

Elegir una función según la necesidad

Para seleccionar exactamente una fila por cliente necesitamos ROW_NUMBER con un criterio completo de desempate. Para una tabla de posiciones donde dos productos pueden compartir lugar, RANK o DENSE_RANK suelen representar mejor la intención.

Qué conviene comprobar

Si el ORDER BY de ROW_NUMBER no distingue todas las filas, la asignación entre empates puede cambiar de una ejecución a otra. Agregar una llave estable al final del orden vuelve determinista la elección.

El ORDER BY dentro de OVER controla la clasificación, pero no garantiza el orden visible del resultado. La consulta exterior necesita su propio ORDER BY cuando la presentación debe conservar una secuencia.

Relacionado con

  • OVER y PARTITION BY: definen el grupo y el orden de la clasificación.
  • CTE: permite conservar únicamente las filas con número 1.
  • TOP: limita filas, pero no clasifica independientemente cada grupo.
  • OFFSET y FETCH: dividen un resultado ordenado en páginas.