Обработка совпадений среди первых N строк
Узнайте, когда использовать RANK или DENSE_RANK, чтобы включить все строки с одинаковыми значениями.
«Обработка совпадений среди первых N строк» — бесплатный урок SQL Interview Prep на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Interview Prep содержит 4 уроков всего.
Дополнительный вопрос о ничьей
После того как Вы уверенно решаете задачи top-N с помощью ROW_NUMBER, интервьюер усложняет вопрос: «Что, если на границе окажутся два сотрудника с абсолютно одинаковой зарплатой? Следует ли включить обоих?»
Это отличает кандидатов, заучивших один запрос, от тех, кто понимает семантику ранжирования. Ответ зависит от выбора между ROW_NUMBER, RANK и DENSE_RANK. В этом уроке показано, какой функции соответствует каждый вариант бизнес-требования.
ROW_NUMBER скрывает ничьи
ROW_NUMBER присваивает каждой строке строго уникальное целое число. Даже если значения в столбце сортировки совпадают, одна строка получит 2, а другая — 3 в произвольном порядке.
Следствие: фильтр WHERE rn <= 3 возвращает ровно 3 строки и может незаметно исключить строку с таким же значением, которую, возможно, следовало включить. Это подходит, когда действительно требуется фиксированное количество строк, но неверно, когда должны пройти все строки с одинаковыми значениями.
-- Salaries: 100, 90, 90, 80
-- ROW_NUMBER -> 1, 2, 3, 4 (the two 90s get 2 and 3 arbitrarily)
ROW_NUMBER() OVER (ORDER BY salary DESC)RANK оставляет пропуски
RANK присваивает строкам с одинаковыми значениями одинаковый ранг, а затем пропускает следующие значения, учитывая ничьи. Две строки, занявшие 2-е место, обе получают 2, а следующая строка получает ранг 4, а не 3.
Используйте RANK, когда нужны «топ-3 позиции» и одну позицию могут занимать несколько человек, как в настоящих турнирных таблицах, где два серебряных призёра означают отсутствие бронзового.
-- Salaries: 100, 90, 90, 80
-- RANK -> 1, 2, 2, 4 (rank 3 is skipped)
RANK() OVER (ORDER BY salary DESC)DENSE_RANK не оставляет пропусков
DENSE_RANK также присваивает строкам с одинаковыми значениями одинаковый ранг, но после этого не пропускает значения. Две строки с рангом 2 обе получают 2, а следующее отличающееся значение получает 3.
Используйте DENSE_RANK, когда вопрос касается различающихся значений: «трёх самых высоких различных уровней зарплаты» или «трёх лучших ценовых категорий». Эта функция считает уникальные значения, а не строки.
-- Salaries: 100, 90, 90, 80
-- DENSE_RANK -> 1, 2, 2, 3 (no gap)
DENSE_RANK() OVER (ORDER BY salary DESC)Сравнение бок о бок
Для списка зарплат 100, 90, 90, 80 три функции дают такие результаты:
- ROW_NUMBER: 1, 2, 3, 4
- RANK: 1, 2, 2, 4
- DENSE_RANK: 1, 2, 2, 3
Эту таблицу стоит выучить наизусть. Интервьюер может просто попросить заполнить её для заданного списка, и именно правильное понимание пропусков является главным.
SELECT salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS rn,
RANK() OVER (ORDER BY salary DESC) AS rnk,
DENSE_RANK() OVER (ORDER BY salary DESC) AS drnk
FROM employees;Включение всех строк с одинаковым рангом с помощью RANK
Чтобы ответить на вопрос «топ-3, но включить всех, кто разделяет граничное место», используйте фильтр RANK() <= 3. Если два человека занимают 3-е место, появятся оба, поэтому результат может содержать более 3 строк.
Именно это подразумевается в формулировке «топ-3 позиции», а не «любые 3 строки». Явно укажите, что при наличии одинаковых значений количество строк может превышать N.
WITH ranked AS (
SELECT name, department, salary,
RANK() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rnk
FROM employees
)
SELECT name, department, salary, rnk
FROM ranked
WHERE rnk <= 3
ORDER BY department, rnk;Три лучших различных уровня с помощью DENSE_RANK
Если требуется получить «три лучших различных уровня зарплаты» и включить всех сотрудников на этих трёх уровнях, используйте DENSE_RANK() <= 3.
В результате может оказаться много строк: все сотрудники из трёх самых высоких уровней оплаты, сколько бы их ни было на каждом уровне. Главное понять, что DENSE_RANK считает различные значения, поэтому фильтр выбирает уровни значений, а не фиксированное количество строк.
WITH ranked AS (
SELECT name, salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS drnk
FROM employees
)
SELECT name, salary, drnk
FROM ranked
WHERE drnk <= 3
ORDER BY drnk;Как выбрать функцию
Соотнесите формулировку с функцией:
- «Ровно N строк» или постраничная выдача →
ROW_NUMBER(добавьте критерий разрешения ничьей). - «Топ-N позиций, одинаковые значения занимают одну позицию» →
RANK. - «Топ-N различных значений / уровней / категорий» →
DENSE_RANK.
Если требования неоднозначны, спросите: «Если на границе возникнет ничья, следует ли включить все строки с одинаковым значением или ограничить результат N строками?» Такой уточняющий вопрос сам по себе принесёт дополнительные баллы.
Критерии разрешения ничьей всё ещё важны
Даже при использовании RANK или DENSE_RANK добавление вторичного ключа в ORDER BY управляет порядком отображения строк с одинаковыми значениями и сохраняет стабильность вывода. Оно не меняет состав строк, попадающих в результат, но делает результаты воспроизводимыми.
При использовании ROW_NUMBER этот критерий необходим, потому что именно он определяет, какая строка с одинаковым значением вообще останется после отсечения по rn.
RANK() OVER (
PARTITION BY department
ORDER BY salary DESC, name ASC
) AS rnkРазобранный пример: лидеры по очкам
В таблице game_scores есть столбцы player и score. Интервьюер хочет получить «всех игроков, занявших первые 2 места по очкам». Если два игрока делят первое место, оба получают позицию 1, а следующий отличный результат — позицию 2.
«Позиции» без пропусков после ничьей указывают на DENSE_RANK. Если бы вместо этого было сказано «топ-2 места с пропусками», следовало бы выбрать RANK. Внимательно слушайте точную формулировку.
WITH r AS (
SELECT player, score,
DENSE_RANK() OVER (ORDER BY score DESC) AS pos
FROM game_scores
)
SELECT player, score, pos
FROM r
WHERE pos <= 2
ORDER BY pos, player;Распространённые ошибки
Ошибки, на которые обращают внимание интервьюеры:
- Использование
ROW_NUMBER, когда задача требует включить строки с одинаковыми значениями, из-за чего подходящая строка незаметно исключается. - Непонимание различий в поведении пропусков у
RANKиDENSE_RANK. - Забыть, что фильтры
RANK/DENSE_RANKмогут вернуть более N строк, а затем объявить результат неверным. - Пропуск
PARTITION BY, когда top-N нужно вычислять для каждой группы, из-за чего вместо этого ранжируется вся таблица.
Быстрая проверка
Выберите подходящую функцию ранжирования для описанного поведения при совпадающих значениях.
Итоги: обработка совпадающих значений
Три функции — три варианта обработки одинаковых значений:
- ROW_NUMBER: всегда уникальные номера, ровно N строк, порядок среди совпадений определяется произвольно.
- RANK: одинаковые значения получают один ранг, после них появляются пропуски.
- DENSE_RANK: одинаковые значения получают один ранг, пропусков нет, подсчитываются различные значения.
Выбирайте функцию согласно формулировке вопроса, задавайте уточняющий вопрос, если она неоднозначна, и помните: фильтры по RANK/DENSE_RANK могут по замыслу возвращать больше N строк.
Часто задаваемые вопросы
Урок «Обработка совпадений среди первых N строк» бесплатный?
Да — полный текст урока «Обработка совпадений среди первых N строк» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Interview Prep, подпишись на CoddyKit PRO. Курс SQL Interview Prep содержит 4 уроков всего.
Чему я научусь в уроке «Обработка совпадений среди первых N строк»?
Узнайте, когда использовать RANK или DENSE_RANK, чтобы включить все строки с одинаковыми значениями. Ты практикуешь SQL Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать SQL Interview Prep?
Предыдущий опыт не требуется. SQL Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Обработка совпадений среди первых N строк»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке SQL Interview Prep?
Да. Каждый урок SQL Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Первые N строк в каждой группе с ROW_NUMBER
- Обработка совпадений среди первых N строк
- Безопасное удаление дубликатов строк
- Сохранение последней строки для каждого ключа