Как найти N-ую максимальную в SQL
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это знать
«Найди вторую по величине зарплату» — легендарный вопрос на собесах аналитика. Простой на первый взгляд, но ловушка: как обрабатывать повторы? 2-ая максимальная — это второе уникальное значение или второй ряд по убыванию?
Умение озвучить 3 способа и выбрать правильный = +1 балл за SQL-секцию.
Способ 1: DENSE_RANK (правильный)
WITH ranked AS (
SELECT name, salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employees
)
SELECT * FROM ranked WHERE rnk = 2;DENSE_RANK трактует одинаковые зарплаты как одно место. «2-ая максимальная» = второе по величине уникальное значение.
Способ 2: LIMIT + OFFSET
SELECT DISTINCT salary
FROM employees
ORDER BY salary DESC
LIMIT 1 OFFSET 1;Простой и читаемый. Работает на Postgres, MySQL, SQLite.
В MSSQL:
SELECT salary FROM (
SELECT DISTINCT salary FROM employees
) s
ORDER BY salary DESC
OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY;Способ 3: подзапрос
SELECT MAX(salary) FROM employees
WHERE salary < (SELECT MAX(salary) FROM employees);Читается как «максимум того, что меньше максимума». Работает для 2-ой, но для N-ой — громоздко.
Для N-ой
С DENSE_RANK
WITH ranked AS (
SELECT name, salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employees
)
SELECT * FROM ranked WHERE rnk = 5;Легко подставить любое N.
Через OFFSET
SELECT DISTINCT salary FROM employees
ORDER BY salary DESC LIMIT 1 OFFSET 4; -- 5-аяDENSE_RANK vs RANK vs ROW_NUMBER
Если две зарплаты одинаковые (10000, 10000, 8000):
| Функция | Результат |
|---|---|
| ROW_NUMBER | 1, 2, 3 — уникальные номера |
| RANK | 1, 1, 3 — ties + пропуск |
| DENSE_RANK | 1, 1, 2 — ties без пропуска |
Для «N-ая максимальная» — нужен DENSE_RANK (если ties считать одним местом).
Если ожидается несколько с одной зарплатой
Все сотрудники со 2-ой по величине зарплатой:
WITH ranked AS (
SELECT name, salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employees
)
SELECT name, salary FROM ranked WHERE rnk = 2;Вернёт всех с ties.
Если нет 2-ой
Все сотрудники с одной зарплатой → 2-ой нет → результат пустой. Иногда ожидают NULL:
SELECT
(SELECT MAX(salary) FROM employees
WHERE salary < (SELECT MAX(salary) FROM employees))
AS second_max;Даже если нет — вернёт NULL, не пустой set.
На собесе
«Найди вторую максимальную зарплату».
Начните с DENSE_RANK — самое универсальное. Упомяните OFFSET как альтернативу. Уточните: «как обрабатывать ties? одинаковые зарплаты — это одно место или нет?».
Junior пишет LIMIT 2,1 без DISTINCT → не учтены ties → неправильно.
Топ-N в каждой группе
Расширение задачи: top-3 зарплаты в каждом department:
WITH ranked AS (
SELECT name, department, salary,
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rnk
FROM employees
)
SELECT * FROM ranked WHERE rnk <= 3;PARTITION BY вместо общей ranking.
Связанные темы
FAQ
А если нужна 1-ая?
MAX(salary). Тривиально.
Что лучше — DENSE_RANK или OFFSET?
OFFSET проще, но DENSE_RANK явно показывает ties.
Работает в MySQL 5.7?
Window functions только с 8.0. В 5.7 — OFFSET или подзапрос.
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.