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;| nombre | precio |
|---|---|
| Lenovo ThinkPad X1 | 1549.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;| nombre | categoria_nombre | precio |
|---|---|---|
| Samsung Galaxy S24 | Smartphones | 899.99 |
| Lenovo ThinkPad X1 | Portátiles | 1549.00 |
| Pantalón chino | Ropa hombre | 49.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;| precio | row_num | rank_val | dense_rank_val |
|---|---|---|---|
| 1000 | 1 | 1 | 1 |
| 900 | 2 | 2 | 2 |
| 900 | 3 | 2 | 2 |
| 800 | 4 | 4 | 3 |
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:
Escrito por Eduardo Lázaro
