Seleccionar el segundo valor más alto

Encontrar el segundo salario más alto, el segundo producto más caro, o en general el N-ésimo valor más grande de una columna es una pregunta clásica tanto en entrevistas técnicas como en escenarios reales de negocio. MySQL ofrece varias técnicas para resolver este problema, desde subconsultas simples hasta funciones de ventana.

Con LIMIT y OFFSET

La forma más directa es ordenar de mayor a menor y saltar al segundo registro:

SELECT DISTINCT precio
FROM productos
ORDER BY precio DESC
LIMIT 1 OFFSET 1;
precio
1549.00

LIMIT 1 OFFSET 1 salta el primer resultado (el más alto) y devuelve el siguiente. El DISTINCT es importante para manejar empates: si dos productos tienen el precio más alto, sin DISTINCT el segundo resultado sería otro producto con el mismo precio máximo, no el segundo valor distinto más alto.

Para el N-ésimo valor, ajusta el OFFSET:

-- Tercer precio más alto
SELECT DISTINCT precio FROM productos ORDER BY precio DESC LIMIT 1 OFFSET 2;
 
-- Quinto precio más alto
SELECT DISTINCT precio FROM productos ORDER BY precio DESC LIMIT 1 OFFSET 4;

Con subconsulta y MAX

Otra técnica clásica usa MAX() excluyendo el valor máximo:

SELECT MAX(precio) AS segundo_mas_alto
FROM productos
WHERE precio < (SELECT MAX(precio) FROM productos);
segundo_mas_alto
1549.00

La subconsulta obtiene el precio máximo (1899.99), y la consulta externa busca el máximo entre todos los precios menores a ese valor. Esta técnica es clara y eficiente, pero se vuelve incómoda para el N-ésimo valor porque necesitarías anidar múltiples subconsultas.

Con funciones de ventana

Las funciones de ventana ofrecen la solución más flexible y legible:

SELECT precio
FROM (
    SELECT DISTINCT precio,
        DENSE_RANK() OVER (ORDER BY precio DESC) AS ranking
    FROM productos
) ranked
WHERE ranking = 2;
precio
1549.00

DENSE_RANK() asigna un rango basado en el valor del precio. Los valores iguales reciben el mismo rango, y el siguiente valor distinto recibe el rango consecutivo. Para encontrar el N-ésimo valor, simplemente cambia el filtro WHERE ranking = N.

Encontrar el producto con el segundo precio más alto

Las técnicas anteriores devuelven solo el valor. Si quieres el registro completo:

SELECT nombre, precio
FROM (
    SELECT nombre, precio,
        DENSE_RANK() OVER (ORDER BY precio DESC) AS ranking
    FROM productos
) ranked
WHERE ranking = 2;
nombreprecio
Lenovo ThinkPad X11549.00

Si hay varios productos con el segundo precio más alto, todos aparecen en el resultado.

El N-ésimo valor por grupo

Para encontrar el segundo producto más caro de cada categoría:

SELECT nombre, categoria_nombre, precio
FROM (
    SELECT
        p.nombre,
        c.nombre AS categoria_nombre,
        p.precio,
        DENSE_RANK() OVER (
            PARTITION BY p.categoria_id
            ORDER BY p.precio DESC
        ) AS ranking
    FROM productos p
    JOIN categorias c ON p.categoria_id = c.id
) ranked
WHERE ranking = 2;
nombrecategoria_nombreprecio
Samsung Galaxy S24Smartphones899.99
Lenovo ThinkPad X1Portátiles1549.00
Pantalón chinoRopa hombre49.99

PARTITION BY categoria_id reinicia el ranking para cada categoría, lo que da el segundo valor más alto dentro de cada grupo.

Diferencia entre RANK, DENSE_RANK y ROW_NUMBER

Supongamos estos precios: 1000, 900, 900, 800.

SELECT
    precio,
    ROW_NUMBER() OVER (ORDER BY precio DESC) AS row_num,
    RANK() OVER (ORDER BY precio DESC) AS rank_val,
    DENSE_RANK() OVER (ORDER BY precio DESC) AS dense_rank_val
FROM (
    SELECT 1000 AS precio
    UNION ALL SELECT 900
    UNION ALL SELECT 900
    UNION ALL SELECT 800
) precios;
preciorow_numrank_valdense_rank_val
1000111
900222
900322
800443

Para "el segundo valor más alto", DENSE_RANK es lo más apropiado: el segundo valor distinto es 900, y 800 es el tercero. Con RANK, 800 sería el cuarto (salta el 3). Con ROW_NUMBER, no hay concepto de empate.

Sin funciones de ventana (versiones anteriores a 8.0)

En versiones de MySQL anteriores a 8.0 sin funciones de ventana, puedes usar una subconsulta correlacionada:

SELECT DISTINCT p1.precio
FROM productos p1
WHERE 2 = (
    SELECT COUNT(DISTINCT p2.precio)
    FROM productos p2
    WHERE p2.precio >= p1.precio
);

Esta consulta cuenta cuántos precios distintos son mayores o iguales al precio actual. Si exactamente 2 precios son mayores o iguales (el propio y el más alto), entonces es el segundo más alto.

Para el N-ésimo valor, cambia el 2 por N:

-- Tercer precio más alto
SELECT DISTINCT p1.precio
FROM productos p1
WHERE 3 = (
    SELECT COUNT(DISTINCT p2.precio)
    FROM productos p2
    WHERE p2.precio >= p1.precio
);

Caso con NULL

Si la columna contiene valores NULL, las funciones MAX() y DENSE_RANK() los ignoran automáticamente. Sin embargo, con LIMIT ... OFFSET, los NULL aparecen al final del ordenamiento descendente y no afectan el resultado.

-- NULL no afecta al resultado
SELECT MAX(precio) FROM productos WHERE precio < (SELECT MAX(precio) FROM productos);

Encontrar el N-ésimo valor más alto es un problema resuelto de forma elegante con DENSE_RANK() en MySQL 8.0+. Para casos simples donde N es pequeño, LIMIT ... OFFSET sigue siendo la solución más directa y eficiente.

Practica con el segundo valor más alto

Usa el editor para encontrar el segundo valor más alto:

Simulador SQL
Ctrl+Enter para ejecutar

Escrito por Eduardo Lázaro