Вторая максимальная зарплата SQL: способы решения
Вторая максимальная зарплата SQL — задача, где сначала нужно решить, считаются ли одинаковые зарплаты одной позицией. Разберём решение через DENSE_RANK, сравним его с ROW_NUMBER и MAX и покажем, как тот же подход обобщается на N-е максимальное значение.
Обновлено · Команда SQL Arena
Что потренируете
- Находить N-е по величине уникальное значение
- Различать DENSE_RANK и ROW_NUMBER при равенствах
- Строить топ-N отдельно внутри каждой группы
- Понимать ограничения MAX и LIMIT/OFFSET
- Проверять запрос на ничьих и коротких группах
Пример задачи в тренажёре
Так выглядит задача в SQL Arena: условие, редактор, результат и схема базы.
Есть отделы и сотрудники. Для каждого отдела нужно вывести сотрудников со второй по величине уникальной зарплатой. Если такую зарплату получают несколько человек, должны попасть все. Верните department, employee и salary.
WITH ranked AS ( SELECT d.name AS department, e.name AS employee, e.salary, DENSE_RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) AS salary_rank FROM employees e JOIN departments d ON d.id = e.department_id ) SELECT department, employee, salary FROM ranked WHERE salary_rank = 2 ORDER BY department, employee;
| # | department | employee | salary |
|---|---|---|---|
| 1 | Analytics | Frank | 125000 |
| 2 | Sales | Bob | 110000 |
| 3 | Sales | Carol | 110000 |
| 4 | Support | Ivan | 85000 |
Есть отделы и сотрудники. Для каждого отдела нужно вывести сотрудников со второй по величине уникальной зарплатой. Если такую зарплату получают несколько человек, должны попасть все. Верните department, employee и salary.
WITH ranked AS ( SELECT d.name AS department, e.name AS employee, e.salary, DENSE_RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) AS salary_rank FROM employees e JOIN departments d ON d.id = e.department_id ) SELECT department, employee, salary FROM ranked WHERE salary_rank = 2 ORDER BY department, employee;
Как найти вторую максимальную зарплату SQL
Сначала строки делятся по отделам через PARTITION BY e.department_id. Это принципиально: рейтинг начинается заново в каждом отделе. Затем зарплаты сортируются по убыванию. Самая высокая получает ранг 1, следующая отличающаяся зарплата — ранг 2. Поэтому Sales с зарплатами 120000, 110000, 110000 и 90000 даёт ранги 1, 2, 2 и 3.
DENSE_RANK выбран из-за ничьих. Bob и Carol получают одинаковые 110000, и оба должны считаться обладателями второй зарплаты. ROW_NUMBER назначил бы им разные номера, например 2 и 3, хотя величина зарплаты у них одна. RANK сохранил бы ничью, но после неё оставил бы разрыв в нумерации; для поиска N-го уникального значения DENSE_RANK обычно выражает условие точнее.
CTE ranked нужен только для того, чтобы вынести вычисленный ранг в отдельный набор строк и затем отфильтровать salary_rank = 2. В том же SELECT нельзя надёжно использовать результат оконной функции в WHERE. Финальный ORDER BY делает результат однозначным: сначала отдел, затем имя сотрудника. В реальной задаче это также помогает стабильно сравнивать ожидаемый вывод.
Для второй максимальной зарплаты важно различать вторую строку и второе уникальное значение. DENSE_RANK() с сортировкой зарплаты по убыванию присваивает одинаковым зарплатам один ранг, поэтому ранг 2 соответствует второй отличающейся величине. ROW_NUMBER(), наоборот, нумерует строки отдельно и может разделить одинаковые значения между разными позициями. Если нужна только вторая уникальная зарплата без дополнительных колонок, задачу можно решать и через вложенные MAX. Для N-го максимального значения оконный подход удобнее обобщается: достаточно выбрать нужный ранг N.
WITH ranked AS ( SELECT d.name AS department, e.name AS employee, e.salary, DENSE_RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) AS salary_rank FROM employees e JOIN departments d ON d.id = e.department_id ) SELECT department, employee, salary FROM ranked WHERE salary_rank = 2 ORDER BY department, employee;
| department | employee | salary |
|---|---|---|
| Analytics | Frank | 125000 |
| Sales | Bob | 110000 |
| Sales | Carol | 110000 |
| Support | Ivan | 85000 |
| Support | Julia | 85000 |
Ошибки при поиске второй максимальной зарплаты
ROW_NUMBER вместо ранга значения
ROW_NUMBER нумерует строки, а не уникальные зарплаты. При двух одинаковых зарплатах одна строка может стать второй, а другая третьей. Такой запрос отвечает на вопрос «какая строка вторая после сортировки», но не всегда на вопрос «какая зарплата вторая по величине».
LIMIT 1 OFFSET 1 без DISTINCT
Для одной общей выборки часто пытаются отсортировать зарплаты и взять вторую строку. Если максимальная зарплата встречается дважды, OFFSET 1 вернёт ту же максимальную величину. Для N-го уникального значения сначала нужен DISTINCT или другая логика, учитывающая равенства.
Один рейтинг для всех отделов
Если убрать PARTITION BY, DENSE_RANK посчитает рейтинг по всей таблице сотрудников. Тогда salary_rank = 2 даст вторую зарплату компании целиком, а не вторую зарплату каждого отдела. Это одна из частых ловушек в формулировках «топ N в группе SQL».
Задачи по теме в тренажёре
Трек «Подготовка к собеседованию»После этого примера полезно сменить формулировку, но оставить тот же принцип. В бесплатной задаче «Пилот рейса со вторым числом пассажиров» нужно распознать вторую позицию в другом наборе данных. Затем можно перейти к задаче «Вторая по величине зарплата», а после неё — к «Три самые высокие зарплаты в каждом отделе», где одиночная N-я позиция превращается в топ-N по каждой группе. Более сложные задачи добавляют временной порядок и необходимость определить, что именно считать «вторым» событием.
- EasyбесплатноПилот рейса со вторым числом пассажировpostgresqlmysqlclickhouse
- EasyбесплатноСоздать книги с обязательной ссылкой на автораpostgresql
- EasyбесплатноАвторы, читавшие собственные статьиpostgresqlmysqlclickhouse
- EasyбесплатноПолки второго уровняpostgresql
- MediumPremiumВторая по величине зарплатаpostgresqlmysqlclickhouse
- HardPremiumСовпадает ли бренд второго товара продавца с любимымpostgresqlmysqlclickhouse
- MediumPremiumТри самые высокие зарплаты в каждом отделеpostgresqlmysql
- HardPremiumПовторные вебхуки за интервал меньше 5 минутpostgresql
Справочник по теме
Подробные статьи с синтаксисом и примерами в справочнике SQL Arena.
Частые вопросы
Как найти вторую максимальную зарплату SQL?
Если нужна вторая уникальная зарплата, можно ранжировать значения через DENSE_RANK() по убыванию и выбрать ранг 2. Другой вариант — найти MAX среди зарплат, которые меньше общего максимума.
Как найти вторую максимальную зарплату при одинаковых значениях?
Если одинаковые зарплаты должны занимать одну позицию, удобно использовать DENSE_RANK. Все одинаковые значения получат один ранг, а следующая отличающаяся зарплата — следующий.
Чем DENSE_RANK отличается от ROW_NUMBER для зарплат?
DENSE_RANK присваивает одинаковым значениям одинаковый ранг. ROW_NUMBER выдаёт каждой строке отдельный номер, даже если зарплаты совпадают.
Как найти N-ю максимальную зарплату SQL?
Можно вычислить DENSE_RANK() по зарплате в порядке DESC и отфильтровать строки по нужному рангу N. Такой вариант естественно работает и для второй, и для любой другой N-й уникальной зарплаты.
Практика задач на вторую максимальную зарплату SQL
В SQL Arena можно решать задачи на ранжирование, N-е значение и топ-N с проверкой на скрытых данных. Если готовитесь к техническим вопросам, продолжите в треке «Подготовка к собеседованию»: там собраны задачи, где важно не только написать запрос, но и правильно разобрать условие. На бесплатном тарифе доступны 292 задачи без карты.
Решать задачи в SQL Arena