Условие
Дана таблица Employee(id int, salary int). Напишите SQL-запрос, который возвращает вторую по величине зарплату. Если такой зарплаты не существует (все одинаковые или одна запись), верните NULL.
Пример
| id | salary |
|---|---|
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
Ожидаемый результат: 200.
| id | salary |
|---|---|
| 1 | 100 |
Ожидаемый результат: NULL.
Решение
Подход 1 — подзапрос с MAX
Берём максимум среди тех, кто меньше глобального максимума. NULL появляется автоматически, если такого нет.
SELECT MAX(salary) AS second_highest
FROM Employee
WHERE salary < (SELECT MAX(salary) FROM Employee);Подход 2 — DISTINCT + LIMIT/OFFSET
SELECT salary
FROM (SELECT DISTINCT salary FROM Employee ORDER BY salary DESC LIMIT 2) t
ORDER BY salary ASC
LIMIT 1;Минус: если запись одна — вернёт ту же зарплату, а не NULL. Нужно дополнительно обработать.
Подход 3 — DENSE_RANK
SELECT salary
FROM (
SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rk
FROM Employee
) t
WHERE rk = 2
LIMIT 1;DENSE_RANK корректно обрабатывает дубликаты: 300, 300, 200 → ранги 1, 1, 2.
Подход «правильный» с гарантированным NULL
SELECT (SELECT DISTINCT salary
FROM Employee
ORDER BY salary DESC
LIMIT 1 OFFSET 1) AS second_highest;Внешний SELECT без FROM превращает «пустую выборку» в NULL.
Подводные камни
ROW_NUMBERвместоDENSE_RANK. Если два сотрудника имеют 300,ROW_NUMBERприсвоит им 1 и 2 — и «вторая» зарплата вернётся как 300, что неправильно.LIMIT 1 OFFSET 1безDISTINCT. Дубликат максимума будет считаться «второй».- NULL-friendly. Подзапрос с
MAXсам отдаётNULL, остальные формы требуют обёртки. - n-я по величине. Замените
< MAX(...)на коррелированный подзапрос сCOUNT(DISTINCT ...)или используйтеDENSE_RANK() = N.
Эталонный ответ
SELECT MAX(salary) FROM Employee WHERE salary < (SELECT MAX(salary) FROM Employee); — самый компактный и NULL-безопасный вариант для второй по величине зарплаты.