Una pregunta clásica en entrevistas técnicas de SQL, Oracle y bases de datos es:
¿Cómo obtener el séptimo salario más alto de una tabla de empleados?
Aunque parece una consulta sencilla, existen varias formas de resolverla. Además, hay una consideración importante: ¿qué sucede cuando varios empleados tienen el mismo salario?
En este artículo utilizaremos una tabla llamada EMPLOYEES con una columna llamada SALARY y veremos diferentes métodos para encontrar el séptimo salario más alto.
Tabla EMPLOYEES
Supongamos que tenemos los siguientes datos:
SELECT employee_id,
first_name,
last_name,
salary
FROM employees;
Ejemplo:
| EMPLOYEE_ID | FIRST_NAME | LAST_NAME | SALARY |
|---|---|---|---|
| 100 | Steven | King | 24000 |
| 101 | Neena | Kochhar | 17000 |
| 102 | Lex | De Haan | 17000 |
| 103 | Alexander | Hunold | 9000 |
| 104 | Bruce | Ernst | 6000 |
| 105 | David | Austin | 4800 |
Nuestro objetivo será ordenar los salarios de mayor a menor y encontrar el que ocupa la posición número 7.
Método 1: utilizando DENSE_RANK()
Una de las mejores formas de resolver este problema es utilizando la función analítica:
DENSE_RANK()
La consulta sería:
SELECT employee_id,
first_name,
last_name,
salary
FROM (
SELECT employee_id,
first_name,
last_name,
salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees
)
WHERE salary_rank = 7;
¿Cómo funciona?
La parte importante es:
DENSE_RANK() OVER (ORDER BY salary DESC)
Primero SQL ordena los salarios de mayor a menor.
Después asigna una posición a cada salario.
Por ejemplo:
SALARY RANK
------ ----
24000 1
17000 2
17000 2
14000 3
13500 4
12000 5
11000 6
10000 7
En este ejemplo, el séptimo salario más alto sería:
10000
La ventaja de DENSE_RANK() es que los salarios duplicados reciben la misma posición.
Por esta razón suele ser la solución más apropiada cuando hablamos del séptimo salario distinto más alto.
Obtener solamente el salario
Si únicamente queremos conocer el valor del séptimo salario más alto:
SELECT DISTINCT salary
FROM (
SELECT salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees
)
WHERE salary_rank = 7;
El resultado podría ser:
SALARY
------
10000
Método 2: utilizando DISTINCT
Otra solución muy sencilla consiste en obtener primero los salarios diferentes.
SELECT salary
FROM (
SELECT DISTINCT salary
FROM employees
ORDER BY salary DESC
)
OFFSET 6 ROWS
FETCH NEXT 1 ROW ONLY;
La lógica es sencilla.
Si queremos encontrar el séptimo salario:
1 → salario más alto
2 → segundo
3 → tercero
4 → cuarto
5 → quinto
6 → sexto
7 → séptimo
Entonces debemos ignorar los primeros seis:
OFFSET 6 ROWS
y recuperar solamente uno:
FETCH NEXT 1 ROW ONLY
Esta sintaxis es especialmente cómoda en versiones modernas de bases de datos que soportan OFFSET y FETCH.
Método 3: usando ROW_NUMBER()
También podemos utilizar:
ROW_NUMBER()
Por ejemplo:
SELECT employee_id,
first_name,
last_name,
salary
FROM (
SELECT employee_id,
first_name,
last_name,
salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS rn
FROM employees
)
WHERE rn = 7;
Sin embargo, aquí existe una diferencia importante.
Supongamos que tenemos:
EMPLOYEE SALARY
-------- ------
John 20000
Mary 18000
Peter 18000
Robert 17000
Con ROW_NUMBER() obtenemos:
SALARY ROW_NUMBER
------ ----------
20000 1
18000 2
18000 3
17000 4
Los empleados que tienen el mismo salario reciben posiciones diferentes.
Por ello, ROW_NUMBER() encuentra el empleado que ocupa físicamente la séptima posición después de ordenar, pero no necesariamente el séptimo salario distinto más alto.
DENSE_RANK vs RANK vs ROW_NUMBER
Esta diferencia es muy importante y aparece frecuentemente en entrevistas técnicas.
Supongamos los siguientes salarios:
10000
9000
9000
8000
7000
Con ROW_NUMBER():
SALARY ROW_NUMBER
------ ----------
10000 1
9000 2
9000 3
8000 4
7000 5
Con RANK():
SALARY RANK
------ ----
10000 1
9000 2
9000 2
8000 4
7000 5
Con DENSE_RANK():
SALARY DENSE_RANK
------ ----------
10000 1
9000 2
9000 2
8000 3
7000 4
Podemos resumirlo así:
| Función | Duplicados | Salta posiciones |
|---|---|---|
ROW_NUMBER() | Posiciones diferentes | No |
RANK() | Misma posición | Sí |
DENSE_RANK() | Misma posición | No |
Para obtener el séptimo salario diferente más alto, normalmente utilizaríamos:
DENSE_RANK()
Método 4: utilizando una subconsulta correlacionada
También podemos resolver el problema sin utilizar funciones analíticas.
SELECT DISTINCT e1.salary
FROM employees e1
WHERE 6 = (
SELECT COUNT(DISTINCT e2.salary)
FROM employees e2
WHERE e2.salary > e1.salary
);
Esta solución es particularmente interesante para entender la lógica detrás del problema.
La consulta busca un salario para el cual existan exactamente:
6 salarios diferentes mayores
Si existen seis salarios mayores, entonces el salario actual ocupa la posición:
7
Conceptualmente:
6 salarios mayores
+
salario actual
=
séptimo salario más alto
Obtener los empleados que reciben el séptimo salario más alto
En muchas situaciones no queremos únicamente conocer el salario.
También queremos saber qué empleados reciben ese salario.
Podemos utilizar:
SELECT employee_id,
first_name,
last_name,
salary
FROM (
SELECT employee_id,
first_name,
last_name,
salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees
)
WHERE salary_rank = 7;
Si varios empleados reciben el mismo séptimo salario, todos aparecerán en el resultado.
Obtener cualquier N salario más alto
Una de las ventajas de esta solución es que podemos adaptarla fácilmente.
Por ejemplo, para obtener el quinto salario más alto:
WHERE salary_rank = 5;
Para obtener el décimo salario más alto:
WHERE salary_rank = 10;
La consulta genérica sería:
SELECT *
FROM (
SELECT employee_id,
first_name,
last_name,
salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees
)
WHERE salary_rank = N;
Donde:
N = posición que queremos encontrar
Los 7 salarios más altos
También podemos modificar la consulta para recuperar los primeros siete salarios:
SELECT employee_id,
first_name,
last_name,
salary
FROM (
SELECT employee_id,
first_name,
last_name,
salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees
)
WHERE salary_rank <= 7
ORDER BY salary DESC;
Esto devuelve todos los empleados cuyos salarios se encuentran entre los siete salarios diferentes más altos de la empresa.
La solución recomendada
Para una base de datos moderna, una solución clara y fácil de mantener sería:
SELECT employee_id,
first_name,
last_name,
salary
FROM (
SELECT employee_id,
first_name,
last_name,
salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees
)
WHERE salary_rank = 7;
Su principal ventaja es que expresa directamente lo que queremos hacer:
EMPLOYEES
│
▼
ORDER BY SALARY DESC
│
▼
DENSE_RANK()
│
▼
RANK = 7
│
▼
7º SALARIO MÁS ALTO
Una pregunta clásica de entrevista SQL
Cuando un entrevistador pregunta:
¿Cómo obtendrías el séptimo salario más alto de la tabla EMPLOYEES?
Antes de escribir la consulta conviene aclarar:
¿Quieres el séptimo empleado después de ordenar los salarios o el séptimo salario distinto más alto?
Esta pequeña diferencia determina qué función debemos utilizar.
Si necesitamos el séptimo registro:
ROW_NUMBER()
Si necesitamos el séptimo salario distinto:
DENSE_RANK()
Comprender esta diferencia demuestra no solamente que sabemos escribir SQL, sino que comprendemos cómo funcionan las funciones analíticas y el manejo de valores duplicados.
Conclusión
SQL ofrece varias formas de obtener el séptimo salario más alto de una tabla.
Podemos utilizar:
DENSE_RANK()
ROW_NUMBER()
RANK()
DISTINCT + OFFSET/FETCH
Subconsultas
Sin embargo, para encontrar el séptimo salario distinto más alto, una de las soluciones más claras es:
DENSE_RANK() OVER (ORDER BY salary DESC)
Las funciones analíticas son herramientas extremadamente poderosas para administradores de bases de datos, desarrolladores y analistas SQL.
Dominar ROW_NUMBER(), RANK() y DENSE_RANK() permite resolver de forma elegante problemas relacionados con Top-N queries, rankings, salarios, ventas, estadísticas y análisis de datos.
