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 — локальная установка не требуется.
Все уроки этого курса
- OVER, PARTITION BY и ORDER BY
- ROW_NUMBER для уникальной нумерации
- RANK и DENSE_RANK при совпадениях
- Фильтрация по результату оконной функции