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, la cláusula que define las filas y el orden del cálculo. La diferencia aparece cuando varias filas comparten la misma posición lógica.

La idea

ROW_NUMBER: asignar un número distinto a cada fila

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

RANK: conservar los empates y dejar huecos

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

DENSE_RANK: conservar los empates sin dejar huecos

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

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 —un valor único que no cambia entre ejecuciones— al final del orden vuelve determinista la elección: el mismo conjunto produce la misma fila seleccionada.

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