0Pricing
Coding Interview Prep · Урок

RANK и DENSE_RANK при совпадениях

Классический вопрос о ранжировании с пропусками и без пропусков при равных значениях.

«RANK и DENSE_RANK при совпадениях» — бесплатный урок Coding Interview Prep на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Coding Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Coding Interview Prep содержит 4 уроков всего.

Сравнение трёх функций ранжирования

SQL предоставляет три оконные функции ранжирования, и самый частый вопрос об окнах — чем они отличаются при совпадениях:

  • ROW_NUMBER — всегда уникальные номера; совпадающие значения получают произвольные разные номера.
  • RANK — совпадающие значения получают один ранг, а следующий ранг пропускается, то есть образуется пропуск.
  • DENSE_RANK — совпадающие значения получают один ранг, а следующий ранг не пропускается, поэтому пропуска нет.

Умение сразу сформулировать это различие — надёжный показатель на собеседовании.

Наглядная таблица примера

Представьте пять результатов, упорядоченных по убыванию: 100, 90, 90, 80, 70. Посмотрите, как каждая функция их пронумерует.

  • ROW_NUMBER: 1, 2, 3, 4, 5
  • RANK: 1, 2, 2, 4, 5 — после совпадения пропускается 3
  • DENSE_RANK: 1, 2, 2, 3, 4 — пропуска нет

Запомните пример 100/90/90/80/70: он позволяет сразу ответить на этот вопрос.

SELECT
  score,
  ROW_NUMBER()  OVER (ORDER BY score DESC) AS rownum,
  RANK()        OVER (ORDER BY score DESC) AS rnk,
  DENSE_RANK()  OVER (ORDER BY score DESC) AS dense
FROM scores;

Как RANK формирует пропуск

RANK присваивает каждой строке ранг, равный единице плюс числу строк, которые строго предшествуют ей в заданном порядке. Перед двумя строками со значением 90 стоит только одна строка — 100, поэтому обе получают ранг 2. Перед следующим, меньшим значением стоят уже три строки, поэтому оно получает ранг 4.

Именно поэтому RANK похож на систему спортивного зачёта: несколько серебряных призёров делят место, а бронзового места нет.

-- scores 100,90,90,80 -> RANK = 1,2,2,4
SELECT score, RANK() OVER (ORDER BY score DESC) AS rnk
FROM scores;

Как DENSE_RANK избегает пропуска

DENSE_RANK присваивает ранг, равный единице плюс числу различных значений, предшествующих текущему значению. После совпадения на 90 перед 80 были только два различных значения — 100 и 90, поэтому 80 получает ранг 3, без пропуска.

Используйте DENSE_RANK, когда важны позиции различных значений, например при поиске «второй по величине зарплаты», где совпадающие значения должны считаться один раз.

-- scores 100,90,90,80 -> DENSE_RANK = 1,2,2,3
SELECT score, DENSE_RANK() OVER (ORDER BY score DESC) AS dense
FROM scores;

Когда выбирать каждую функцию

Краткое руководство по выбору, которое обычно хотят услышать на собеседовании:

  • Нужна ровно одна строка на позицию (последняя запись, постраничная навигация)? → ROW_NUMBER.
  • Нужна таблица результатов, где одинаковые значения получают один ранг, а пропуски учитываются правильно (спортивный рейтинг, первые 3 места с учётом равенств)? → RANK.
  • Нужно N-е различное значение (N-я по величине зарплата)? → DENSE_RANK.

Неправильный выбор изменит набор возвращаемых строк, поэтому обязательно вслух объясните свой выбор.

Ранжирование внутри разделов

Все три функции принимают PARTITION BY и независимо выполняют ранжирование внутри каждой группы. В каждой группе нумерация рангов начинается с 1.

Здесь для каждого отдела его сотрудники ранжируются по зарплате, а одинаковые значения обрабатываются с помощью DENSE_RANK. Самый высокооплачиваемый сотрудник каждого отдела получает ранг 1, а сотрудники с одинаковой зарплатой — один и тот же ранг.

SELECT
  department,
  name,
  salary,
  DENSE_RANK() OVER (
    PARTITION BY department
    ORDER BY salary DESC
  ) AS salary_rank
FROM employees;

Разбор примера: первые 3 места с учётом равенств

«Верните 3 самые высокие зарплаты в каждом отделе, а если несколько сотрудников делят третье место, включите их всех». Для этого требуется RANK или DENSE_RANK, но не ROW_NUMBER.

Использование DENSE_RANK <= 3 оставляет три самых высоких уровня зарплаты без повторов и всех сотрудников с такими зарплатами — именно это и означает «с учётом равенств».

SELECT department, name, salary, salary_rank
FROM (
  SELECT department, name, salary,
         DENSE_RANK() OVER (
           PARTITION BY department ORDER BY salary DESC
         ) AS salary_rank
  FROM employees
) t
WHERE salary_rank <= 3;

Разбор примера: вторая по величине зарплата

Самый известный вопрос на собеседованиях по SQL. Надёжное решение использует DENSE_RANK, чтобы повторяющиеся максимальные зарплаты не вытеснили настоящее второе значение.

Если три человека получают максимальную зарплату, DENSE_RANK = 2 по-прежнему правильно определит следующую отличающуюся зарплату, тогда как ROW_NUMBER = 2 просто вернёт ещё одного сотрудника с максимальной зарплатой.

SELECT DISTINCT salary AS second_highest
FROM (
  SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS dr
  FROM employees
) t
WHERE dr = 2;

RANK и DENSE_RANK для «N-го по величине»

Почему для задач на N-е по величине значение следует выбрать DENSE_RANK, а не RANK? Потому что RANK оставляет пропуски. При зарплатах 100, 100 и 90 значение 90 получает RANK = 3, но DENSE_RANK = 2.

Если вопрос звучит как «2-я по величине отличающаяся зарплата», фильтрация по RANK = 2 не вернёт ничего, а DENSE_RANK = 2 правильно вернёт 90. Всегда сопоставляйте «N-е различное значение» с DENSE_RANK.

-- salaries 100,100,90
-- RANK:       1,1,3   -> WHERE rank = 2 returns NOTHING
-- DENSE_RANK: 1,1,2   -> WHERE dense_rank = 2 returns 90

Примечания о различиях между диалектами

Хорошая новость: ROW_NUMBER, RANK и DENSE_RANK соответствуют стандарту SQL и работают одинаково в PostgreSQL, SQL Server, Oracle, MySQL 8+, BigQuery и Snowflake. Приведённые выше правила обработки равенств универсальны.

Связанная функция PERCENT_RANK использует RANK для вычисления относительной позиции от 0 до 1 — о ней стоит упомянуть, если интервьюер спросит о ранжировании по процентилям.

Распространённые ошибки

Обратите внимание на следующие ошибки:

  • Использование ROW_NUMBER для поиска «N-го по величине» и потеря строк с одинаковыми значениями.
  • Использование RANK, когда в вопросе речь идёт о различных значениях, что приводит к пустому результату из-за пропусков.
  • Забытый ORDER BY внутри OVER — без него ранжирование не имеет смысла.
  • Фильтрация ранга напрямую в WHERE, а не во внешнем запросе.

Быстрая проверка

Примените правила обработки равенств.

Итоги: с пропусками и без них

Теперь Вы можете мгновенно выбрать правильную функцию ранжирования:

  • ROW_NUMBER: уникальные номера, общих рангов нет.
  • RANK: одинаковые значения получают один ранг, затем появляется пропуск (1,2,2,4).
  • DENSE_RANK: одинаковые значения получают один ранг, пропусков нет (1,2,2,3).
  • «N-е по величине отличающееся значение» → DENSE_RANK; «первые N значений с учётом равенств» → RANK/DENSE_RANK; «одна строка на позицию» → ROW_NUMBER.

Далее Вы узнаете, как именно фильтровать результат оконной функции, поскольку сделать это в WHERE нельзя.

Часто задаваемые вопросы

Урок «RANK и DENSE_RANK при совпадениях» бесплатный?

Да — полный текст урока «RANK и DENSE_RANK при совпадениях» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Coding Interview Prep, подпишись на CoddyKit PRO. Курс Coding Interview Prep содержит 4 уроков всего.

Чему я научусь в уроке «RANK и DENSE_RANK при совпадениях»?

Классический вопрос о ранжировании с пропусками и без пропусков при равных значениях. Ты практикуешь Coding Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Coding Interview Prep?

Предыдущий опыт не требуется. Coding Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «RANK и DENSE_RANK при совпадениях»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Coding Interview Prep?

Да. Каждый урок Coding Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. OVER, PARTITION BY и ORDER BY
  2. ROW_NUMBER для уникальной нумерации
  3. RANK и DENSE_RANK при совпадениях
  4. Фильтрация по результату оконной функции
← Назад к Coding Interview Prep