Mantén SQLtest vivo: Ayuda a cubrir los costos del servidor de este mes de $100 para mantener todas las lecciones y el playground de SQL en línea. Apoya el proyecto →
Código SQL copiado al portapapeles

Lección 12.4 · Tiempo de lectura: ~7 min

"Encuentra el segundo salario más alto" es una de las preguntas más frecuentes en las entrevistas de SQL, y normalmente le siguen "ahora el N-ésimo" y "ahora en cada departamento". En esta lección la resolverás de cinco formas: MAX con una subconsulta, LIMIT ... OFFSET, DENSE_RANK() y una subconsulta correlacionada, y verás las trampas que buscan los entrevistadores: salarios repetidos, un N-ésimo valor que no existe y los grupos.

N-ésimo salario más alto en SQL

El ejercicio parece trivial, y precisamente por eso es tan popular: el primer intento suele funcionar con los datos de ejemplo y fallar en un caso límite. Una buena respuesta tiene en cuenta tres cosas: los empates (dos empleados con el mismo salario), la falta de respuesta (menos de N salarios distintos) y los grupos (el N-ésimo salario de cada departamento).

Los ejemplos usan la sintaxis de MySQL 8.0 y crean su propia tabla, así que ejecútalos en el playground en una base sin datos de ejemplo.

Datos de ejemplo

CREATE TABLE employee (
    id INT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    department VARCHAR(30) NOT NULL,
    salary INT NOT NULL
);

INSERT INTO employee VALUES
    (1, 'Alice', 'Sales', 5000),
    (2, 'Bob', 'Sales', 7000),
    (3, 'Carol', 'Sales', 7000),
    (4, 'Dave', 'IT', 9000),
    (5, 'Eve', 'IT', 6500),
    (6, 'Frank', 'IT', 9000),
    (7, 'Grace', 'HR', 4800),
    (8, 'Heidi', 'HR', 5200);

Los salarios distintos, de mayor a menor, son 9000, 7000, 6500, 5200, 5000, 4800. Dos personas ganan 9000 y dos ganan 7000, así que el segundo salario más alto es 7000, no 9000.


Solución 1: el MAX por debajo del MAX

El segundo salario más alto es el mayor salario que es menor que el máximo:

SELECT MAX(salary) AS second_highest_salary
FROM employee
WHERE salary < (SELECT MAX(salary) FROM employee);

Resultado: 7000.

Es corta, funciona en cualquier base de datos, maneja los empates y devuelve NULL cuando no hay un segundo salario, porque el MAX de un conjunto vacío es NULL. Su límite: solo sirve para N = 2. Para el tercer salario necesitarías otro nivel de anidación.


Solución 2: DISTINCT con LIMIT y OFFSET

Ordena los salarios de mayor a menor y salta N − 1 de ellos:

SELECT DISTINCT salary
FROM employee
ORDER BY salary DESC
LIMIT 1 OFFSET 1;

Resultado: 7000.

DISTINCT es imprescindible. Sin él, la lista ordenada es 9000, 9000, 7000, … y OFFSET 1 cae en el segundo 9000:

-- Incorrecto: devuelve otra vez el salario máximo por el empate
SELECT salary
FROM employee
ORDER BY salary DESC
LIMIT 1 OFFSET 1;

Resultado: 9000, el salario más alto, no el segundo.

Devolver NULL cuando no hay respuesta

Si N es mayor que el número de salarios distintos, la consulta devuelve cero filas. Los ejercicios de entrevista suelen pedir un NULL en su lugar. Envuelve la consulta en una subconsulta escalar: una subconsulta que no encuentra nada devuelve NULL.

SELECT (
    SELECT DISTINCT salary
    FROM employee
    ORDER BY salary DESC
    LIMIT 1 OFFSET 9
) AS tenth_highest_salary;

Resultado: una fila con NULL; solo hay 6 salarios distintos.

LIMIT ... OFFSET es sintaxis de MySQL, PostgreSQL y SQLite. SQL Server y Oracle 12c+ usan OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY.


Solución 3: DENSE_RANK

Una función de ventana clasifica cada fila y la consulta externa elige el puesto que necesitas. Es la solución más flexible y la que suelen esperar los entrevistadores:

WITH ranked AS (
    SELECT name, salary,
           DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank
    FROM employee
)
SELECT name, salary
FROM ranked
WHERE salary_rank = 2;

Resultado: Bob y Carol, ambos con 7000.

También responde a la pregunta siguiente, "¿quién gana el segundo salario más alto?". Cambia 2 por cualquier N. Aquí solo DENSE_RANK es correcto:

salaryROW_NUMBERRANKDENSE_RANK
9000111
9000211
7000332
7000432
6500553

ROW_NUMBER da números distintos a salarios iguales, y RANK deja huecos después de los empates, así que RANK = 2 no encuentra a nadie. DENSE_RANK numera los valores distintos sin huecos.


Solución 4: subconsulta correlacionada

Antes de las funciones de ventana, la respuesta clásica era: un salario es el N-ésimo más alto si exactamente N salarios distintos son mayores o iguales que él.

SELECT DISTINCT e1.salary
FROM employee e1
WHERE 3 = (
    SELECT COUNT(DISTINCT e2.salary)
    FROM employee e2
    WHERE e2.salary >= e1.salary
);

Resultado: 6500, el tercer salario más alto.

Funciona en cualquier base de datos, pero la subconsulta se ejecuta para cada fila, así que en una tabla grande es lenta. Menciónala como alternativa y usa DENSE_RANK en la práctica.


Solución 5: el N-ésimo en cada grupo

La pregunta siguiente habitual: "el segundo salario más alto de cada departamento". Agrega PARTITION BY a la función de ventana:

WITH ranked AS (
    SELECT department, name, salary,
           DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank
    FROM employee
)
SELECT department, name, salary
FROM ranked
WHERE salary_rank = 2
ORDER BY department;

Resultado: HR – Grace (4800), IT – Eve (6500), Sales – Alice (5000).

En Sales, Bob y Carol comparten el salario máximo, así que el segundo es el de Alice, 5000. Un departamento con un solo salario distinto no aparecería en el resultado.


Qué solución elegir

SoluciónNEmpatesSin respuestaGrupos
MAX por debajo del MAXSolo 2SíNULLDifícil
DISTINCT ... LIMIT OFFSETCualquieraCon DISTINCTVacío; NULL en una subconsulta escalarNo
DENSE_RANK()CualquieraSíVacíoPARTITION BY
Subconsulta correlacionadaCualquieraSíVacíoAgregar una condición

En una entrevista, empieza por DENSE_RANK, explica por qué no ROW_NUMBER ni RANK, y menciona cómo devolverías NULL.

Práctica


Conclusiones clave:

  • El segundo valor más alto es MAX(x) WHERE x < (SELECT MAX(x) ...); maneja los empates y devuelve NULL cuando no existe.
  • Con LIMIT 1 OFFSET N-1, usa siempre DISTINCT, o los empates desplazan la respuesta.
  • Una subconsulta escalar convierte "cero filas" en NULL.
  • DENSE_RANK() resuelve cualquier N, devuelve los empleados y, con PARTITION BY, funciona por grupo.
  • ROW_NUMBER y RANK dan respuestas incorrectas cuando los valores se repiten.

Preguntas frecuentes

¿Cómo encuentro el segundo salario más alto en SQL?

La consulta portable más corta es SELECT MAX(salary) FROM employee WHERE salary < (SELECT MAX(salary) FROM employee);. Devuelve el mayor salario por debajo del máximo, ignora los empates y devuelve NULL si todos ganan lo mismo.

¿Cómo encuentro el N-ésimo salario más alto en SQL?

Usa DENSE_RANK() OVER (ORDER BY salary DESC) en una CTE y filtra el puesto N, o SELECT DISTINCT salary ... ORDER BY salary DESC LIMIT 1 OFFSET N-1. Ambas manejan correctamente los salarios repetidos.

¿Por qué no usar ROW_NUMBER o RANK?

ROW_NUMBER numera de forma distinta salarios iguales, así que un empate arriba coloca el mismo valor en el segundo puesto. RANK salta números después de los empates: con dos salarios máximos, no existe el puesto 2. DENSE_RANK numera los valores distintos de forma consecutiva.

¿Cómo devuelvo NULL si no existe el N-ésimo salario más alto?

Pon la consulta dentro de una subconsulta escalar: SELECT (SELECT DISTINCT salary FROM employee ORDER BY salary DESC LIMIT 1 OFFSET 9) AS result;. Cuando la consulta interna no encuentra nada, la externa igual devuelve una fila con NULL.