ROW_NUMBER для уникальной нумерации
Присваивайте детерминированный номер строки внутри каждого раздела.
«ROW_NUMBER для уникальной нумерации» — бесплатный урок SQL Interview Prep на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Interview Prep содержит 4 уроков всего.
Что гарантирует ROW_NUMBER
ROW_NUMBER() присваивает каждой строке внутри её раздела уникальное целое число без пропусков, начиная с 1 и следуя ORDER BY окна. Две строки никогда не получают один и тот же номер, даже если их значения сортировки совпадают.
Именно поэтому на собеседованиях эту функцию выбирают для задач «выбрать одну строку из каждой группы» и устранения дубликатов. Другие функции ранжирования (RANK, DENSE_RANK) не гарантируют уникальность при совпадениях.
Минимальный запрос с ROW_NUMBER
В простейшей форме функция нумерует весь набор результатов в выбранном порядке. ROW_NUMBER всегда требует ORDER BY внутри OVER: без него нумерация была бы произвольной, а большинство систем либо отклоняет такой запрос, либо выдаёт предупреждение.
В данном случае сотрудник, нанятый позднее всех, получает номер строки 1, если упорядочить данные по дате приёма на работу по убыванию.
SELECT
name,
hire_date,
ROW_NUMBER() OVER (ORDER BY hire_date DESC) AS rn
FROM employees;Нумерация внутри разделов
Добавьте PARTITION BY, чтобы независимо нумеровать строки внутри каждой группы. Счётчик сбрасывается на 1 при каждом новом значении раздела.
В этом примере каждый отдел получает собственную последовательность 1, 2, 3, упорядоченную по зарплате. Самый высокооплачиваемый сотрудник каждого отдела получает номер 1 — это основа шаблона выборки первых N строк в каждой группе.
SELECT
department,
name,
salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS dept_rn
FROM employees;ROW_NUMBER при совпадениях: ничьих не бывает
Критически важный момент для собеседования: если у двух строк одинаковые значения сортировки, ROW_NUMBER всё равно присваивает им разные номера. Какая из них получит меньший номер, недетерминировано, если не добавить столбец для разрешения равенства.
RANKприсвоил бы одинаковым строкам один и тот же ранг.ROW_NUMBERпроизвольно выбирает порядок между ними.
Чтобы результаты можно было воспроизвести, всегда добавляйте уникальный дополнительный ключ в ORDER BY.
SELECT
name,
salary,
ROW_NUMBER() OVER (
ORDER BY salary DESC, employee_id -- employee_id breaks ties deterministically
) AS rn
FROM employees;Правило детерминированного разрешения равенств
Если Ваш ORDER BY не задаёт строгий порядок — то есть ни одна комбинация столбцов не уникальна, — нумерация строк может меняться между запусками даже для одних и тех же данных. На собеседованиях эту ловушку часто проверяют в задачах о постраничной навигации и «последней записи».
Практическое правило: добавляйте первичный ключ или другой уникальный столбец в качестве последнего ключа сортировки, если результат должен быть стабильным.
-- Unstable: many rows can share the same created_at
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)
-- Stable: id guarantees a single deterministic winner
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC, id DESC)Разбор примера: последний заказ каждого клиента
Классическая задача: «Верните самый свежий заказ каждого клиента». Пронумеруйте заказы каждого клиента по дате в порядке убывания, а затем оставьте rn = 1 во внешнем запросе.
Поскольку ROW_NUMBER гарантирует ровно одну строку с номером 1 в каждом разделе, Вы получите ровно одну строку на клиента — без дубликатов, даже если два заказа имеют одну и ту же временную метку: дополнительный ключ разрешает равенство.
SELECT customer_id, order_id, order_date, amount
FROM (
SELECT
customer_id, order_id, order_date, amount,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC, order_id DESC
) AS rn
FROM orders
) t
WHERE rn = 1;Разбор примера: устранение дубликатов строк
ROW_NUMBER — самый удобный инструмент для устранения дубликатов. Разбейте данные на разделы по столбцам, определяющим дубликат, задайте правило предпочтения в сортировке, а затем оставьте rn = 1.
Здесь строки с одинаковым адресом электронной почты считаются дубликатами, и сохраняется созданная раньше всех. Все строки с rn > 1 являются дубликатами, которые можно удалить или проигнорировать.
SELECT id, email, created_at
FROM (
SELECT
id, email, created_at,
ROW_NUMBER() OVER (
PARTITION BY email
ORDER BY created_at ASC, id ASC
) AS rn
FROM users
) t
WHERE rn = 1;ROW_NUMBER для постраничной навигации
До того как OFFSET/FETCH стали универсальными, для постраничной навигации использовался ROW_NUMBER; он по-прежнему применяется в SQL Server и многих заданиях ETL. Строки нумеруют в стабильном порядке, а затем во внешнем запросе фильтруют диапазон номеров.
Важно для собеседования: постраничная навигация корректна только при детерминированном порядке сортировки — иначе одна и та же строка может оказаться на двух страницах или полностью исчезнуть из результатов.
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY created_at, id) AS rn
FROM articles
) t
WHERE rn BETWEEN 21 AND 40; -- page 2, 20 per pageПрисваивание последовательности для изменения порядка данных
Иногда Вам нужен только последовательный индекс: например, чтобы перенумеровать строки после сортировки, создать метку от 1 до N или сопоставить строки со сгенерированным рядом. ROW_NUMBER в нужном порядке создаёт плотную последовательность без пропусков.
Так можно также присвоить произвольному неупорядоченному набору стабильные позиции для последующего соединения.
SELECT
ROW_NUMBER() OVER (ORDER BY score DESC, player_id) AS leaderboard_position,
player_id,
score
FROM scores;ROW_NUMBER и COUNT для «N-й строки»
Если Вас просят найти «третий по свежести заказ» или «вторую строку», используйте ROW_NUMBER и фильтруйте по точному значению во внешнем запросе. Поскольку номера уникальны, rn = 3 возвращает ровно одну строку.
Сравните это с RANK: если Вам нужно второе различное значение, например вторая по величине зарплата с учётом совпадений, ROW_NUMBER — неподходящий инструмент. Здесь нужен DENSE_RANK, который рассматривается в следующем уроке.
SELECT order_id, order_date
FROM (
SELECT order_id, order_date,
ROW_NUMBER() OVER (ORDER BY order_date DESC, order_id DESC) AS rn
FROM orders
) t
WHERE rn = 3; -- exactly the 3rd most recent orderПовторение: распространённые ошибки
Не путайте следующие моменты, даже под давлением собеседования:
ROW_NUMBERвсегда даёт уникальные номера без пропусков внутри раздела.- Ему нужен
ORDER BYвOVER; без уникального дополнительного ключа результаты при совпадениях недетерминированы. - Его нельзя фильтровать в
WHERE— оберните вычисление в подзапрос или CTE. - Используйте его для получения одной строки на группу, устранения дубликатов и постраничной навигации; применяйте
DENSE_RANK, когда одинаковые значения должны получать один номер.
Быстрая проверка
Как ROW_NUMBER обрабатывает строки с одинаковым значением в ORDER BY?
Повторение: детерминированная нумерация
ROW_NUMBER — Ваш инструмент для уникальной нумерации без пропусков внутри разделов. Вы научились:
- Нумеровать целые наборы данных и группы внутри разделов.
- Добавлять уникальный дополнительный ключ для детерминированных и воспроизводимых результатов.
- Решать задачи о последней строке для каждого ключа, устранении дубликатов, постраничной навигации и N-й строке, фильтруя
rnво внешнем запросе.
Далее Вы увидите, как RANK и DENSE_RANK намеренно присваивают одинаковые номера совпадающим значениям и чем различается поведение их пропусков.
Часто задаваемые вопросы
Урок «ROW_NUMBER для уникальной нумерации» бесплатный?
Да — полный текст урока «ROW_NUMBER для уникальной нумерации» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Interview Prep, подпишись на CoddyKit PRO. Курс SQL Interview Prep содержит 4 уроков всего.
Чему я научусь в уроке «ROW_NUMBER для уникальной нумерации»?
Присваивайте детерминированный номер строки внутри каждого раздела. Ты практикуешь SQL Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать SQL Interview Prep?
Предыдущий опыт не требуется. SQL Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «ROW_NUMBER для уникальной нумерации»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке SQL Interview Prep?
Да. Каждый урок SQL Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- OVER, PARTITION BY и ORDER BY
- ROW_NUMBER для уникальной нумерации
- RANK и DENSE_RANK при совпадениях
- Фильтрация по результату оконной функции