SQL-вопросы на собеседовании аналитика данных
SQL·junior

Как найти вторую по величине зарплату?

Короткий ответ

Три рабочих способа: подзапрос max(salary) WHERE salary < (SELECT max(salary)); сортировка ORDER BY salary DESC с LIMIT 1 OFFSET 1; оконная функция DENSE_RANK() OVER (ORDER BY salary DESC) с фильтром = 2. Главная развилка — как считать одинаковые зарплаты: LIMIT/OFFSET даст «вторую строку», DENSE_RANK — «второй уровень значения».

Хрестоматийная задача: у неё три классических решения, и по тому, какое ты выберешь и как оговоришь ничьи, видно глубину понимания. «Правильного» одного ответа тут нет — есть правильные уточнения.

Как рассуждать

Сначала спроси: что значит «вторая» — вторая по порядку строк или второе по величине значение зарплаты? Если у максимума две ничьи (двое получают по 100), то «вторая строка» — это тоже 100, а «второе значение» — уже 90. Это разные ответы, и интервьюер обычно хочет второе значение.

Дальше выбирай инструмент под смысл:

  • «максимум среди тех, кто меньше глобального максимума» — надёжно отдаёт именно второе значение;
  • сортировка с пропуском первой строки (LIMIT 1 OFFSET 1) — вернёт вторую строку, ничьи не схлопнет;
  • ранжирование оконной функцией — гибче всего и легко обобщается на N-ю зарплату.

Для примера на нашей схеме «зарплаты» нет, поэтому возьмём аналог — второго по тратам клиента: смысл задачи тот же.

Эталонный ответ

Способ 1 — подзапрос, «максимум ниже максимума». Явно отдаёт второе значение:

SELECT max(spend) AS second_spend
FROM (
  SELECT customer_id, sum(amount) AS spend
  FROM payment GROUP BY customer_id
) t
WHERE spend < (SELECT max(spend) FROM (
  SELECT sum(amount) AS spend FROM payment GROUP BY customer_id
) x);

Способ 2 — сортировка с пропуском. Коротко, но вернёт вторую строку, а не второе значение (при ничьей на первом месте это может быть та же сумма):

SELECT customer_id, sum(amount) AS spend
FROM payment
GROUP BY customer_id
ORDER BY spend DESC
LIMIT 1 OFFSET 1;

Способ 3 — оконная функция, самый гибкий:

WITH ranked AS (
  SELECT customer_id,
         sum(amount) AS spend,
         dense_rank() OVER (ORDER BY sum(amount) DESC) AS rnk
  FROM payment
  GROUP BY customer_id
)
SELECT customer_id, spend FROM ranked WHERE rnk = 2;

Ключевой момент — выбор ранжирующей функции. DENSE_RANK вернёт всех со «вторым по величине» значением (второй уровень). RANK при ничьей на первом месте пропустит номер 2. ROW_NUMBER даст ровно одну строку. Разница между ними — в отдельном разборе, ROW_NUMBER, RANK, DENSE_RANK.

Сильный ответ проговаривает поведение при ничьих и то, что оконный способ элементарно обобщается на N-ю зарплату заменой = 2 на нужное число.

Частые ошибки

  • Дают только LIMIT 1 OFFSET 1, не оговаривая ничьи и дубли — при равных максимумах ответ окажется неверным по смыслу.
  • Используют ROW_NUMBER там, где ждут «все со второй зарплатой», и теряют строки при ничьей.
  • Пишут WHERE salary = (SELECT max(...)) - 1 — предполагают шаг 1 между значениями, чего в реальных данных нет.
  • Забывают, что при пустой второй позиции (все зарплаты равны) запрос вернёт пусто — и не проговаривают этот крайний случай.

Потренируй этот вопрос с ИИ-интервьюером

Читать разбор полезно, а отвечать вслух под давлением — совсем другое. ИИ-интервьюер задаст этот и смежные вопросы, будет копать глубже и разберёт твои ответы. Первое короткое интервью — бесплатно.

Пройти собеседование бесплатно

Смежные вопросы