← все задачи

SQL · задача 1 из 10

Вторая по величине зарплата

Начальный 10 минут DISTINCTLIMIT/OFFSETNULL

Условие

В таблице employees есть поле salary. Верните вторую по величине зарплату. Если такой нет — верните NULL, а не пустой результат.

Что требуется

  • Одинаковые зарплаты считаются одной величиной
  • При отсутствии второй зарплаты — строка с NULL
  • Решение работает на таблице любого размера

Пример

salary: 100, 100, 90, 80
-> 90

salary: 100, 100
-> NULL

Сначала уточните

Вопросы до кода — половина оценки. Молча начать печатать хуже, чем задать два вопроса.

  • Вторая по величине или зарплата второго сотрудника? При дубликатах это разные ответы
  • Нужна ли зарплата в разрезе отделов — это уже другая задача, с оконной функцией
  • Что должно вернуться при пустой таблице?
Показать решение Скрыть решение

Решение

-- Вариант 1: подзапрос гарантирует NULL вместо пустого результата
SELECT (
    SELECT DISTINCT salary
    FROM employees
    ORDER BY salary DESC
    LIMIT 1 OFFSET 1
) AS second_salary;

-- Вариант 2: через оконную функцию, если нужен и сотрудник
SELECT salary
FROM (
    SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rank
    FROM employees
) ranked
WHERE rank = 2
LIMIT 1;

Почему так

Почему DISTINCT обязателен

  • Без него при двух одинаковых максимумах «вторая» окажется тем же числом, что и первая
  • Именно на этом примере задачу и проверяют: 100, 100, 90 должно дать 90
  • В варианте с рангом ту же роль играет DENSE_RANK: он не пропускает номера при дубликатах

Почему внешний SELECT вокруг подзапроса

  • Запрос с LIMIT 1 OFFSET 1 на короткой таблице вернёт ноль строк, а по условию нужна строка с NULL
  • Скалярный подзапрос без строк даёт именно NULL — это и требуется
  • Разница между «нет строк» и «строка с NULL» ломает клиентский код, который ждёт одно значение

RANK, DENSE_RANK и ROW_NUMBER

  • ROW_NUMBER нумерует подряд, дубликаты получат разные номера — для «второй величины» не подходит
  • RANK при двух первых местах пропустит второе и выдаст 1, 1, 3 — второй зарплаты не найдётся
  • DENSE_RANK даёт 1, 1, 2 — то, что нужно; умение выбрать нужную из трёх и есть проверка

Что спросят дальше

  • Следующий вопрос почти всегда: N-я зарплата по каждому отделу — это PARTITION BY department_id
  • Спросят про MAX(salary) WHERE salary < (SELECT MAX(salary)) — рабочий вариант, но два прохода по таблице
  • Спросят про индекс по salary: для LIMIT 1 OFFSET 1 он превращает сортировку в чтение двух строк

Следующая задача

Топ-3 товара в каждой категории — Самая частая задача на оконные функции: «лучшие внутри группы».