
В SQL отсутствует встроенная функция для явного указания номера строки в стандартном SELECT, однако большинство СУБД предоставляют средства для его вычисления. Для нумерации строк чаще всего используют оконные функции ROW_NUMBER(), RANK() и DENSE_RANK(), которые позволяют формировать последовательность на основе заданного порядка сортировки.
Функция ROW_NUMBER() присваивает уникальный порядковый номер каждой строке в пределах результата запроса. Использование конструкции OVER(ORDER BY колонка) обеспечивает строгую сортировку по выбранной колонке. Например, для получения нумерации клиентов по дате регистрации достаточно добавить ROW_NUMBER() OVER(ORDER BY registration_date) AS row_num.
Если требуется учитывать одинаковые значения в ключевой колонке, применяют RANK() или DENSE_RANK(). RANK() оставляет разрывы в последовательности при повторяющихся значениях, а DENSE_RANK() формирует непрерывную нумерацию. Это важно при анализе топ-N записей по группе или при построении отчетов с группировкой.
Для СУБД без поддержки оконных функций можно использовать подзапросы с агрегатными функциями или переменные, например, в MySQL до версии 8.0 часто применяют @counter, увеличивая его значение для каждой строки. Такой подход требует внимательного контроля сортировки и корректного присвоения номера.
Правильное определение номера строки позволяет фильтровать диапазоны, создавать страницы в пагинации и упрощает обработку выборок в аналитических запросах. Рекомендуется всегда явно указывать ORDER BY в сочетании с нумерацией, чтобы избежать непредсказуемого порядка строк и ошибок при дальнейшем использовании данных.
Использование ROW_NUMBER() для нумерации строк

Функция ROW_NUMBER() присваивает уникальный порядковый номер каждой строке в результате запроса на основе заданного порядка. Синтаксис: ROW_NUMBER() OVER (ORDER BY колонка [ASC|DESC]). Нумерация начинается с 1 и увеличивается на единицу для каждой строки.
Пример: для таблицы employees с колонками id, name, salary можно получить список сотрудников с порядковым номером по убыванию зарплаты:
SELECT id, name, salary, ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num FROM employees;
ROW_NUMBER() особенно полезна для:
- Удаления дубликатов, оставляя только первую запись по заданному критерию.
- Формирования отчетов с точным порядком элементов без изменения исходной таблицы.
Важно: ORDER BY в OVER() определяет последовательность нумерации. Без него ROW_NUMBER() вернет произвольные номера, что может нарушить логику выборки.
Для выборки определенной страницы данных применяется конструкция:
WITH numbered AS (SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS row_num FROM table_name) SELECT * FROM numbered WHERE row_num BETWEEN 11 AND 20;
Использование ROW_NUMBER() упрощает контроль над порядком строк и позволяет управлять выборками без временных таблиц или сложных подзапросов.
Присвоение номера строкам с PARTITION BY
Функция ROW_NUMBER() позволяет нумеровать строки в пределах разделов, создаваемых с помощью PARTITION BY. Это особенно полезно при анализе данных с группировкой по категориям, датам или другим признакам.
Синтаксис выглядит так:
ROW_NUMBER() OVER (PARTITION BY column1, column2 ORDER BY column3)
Примеры применения:
- Выбор первых продаж по каждому менеджеру:
SELECT manager_id, sale_id, ROW_NUMBER() OVER (PARTITION BY manager_id ORDER BY sale_date DESC) AS row_num FROM sales;Результат: каждая строка внутри группы менеджера получает уникальный номер, начиная с 1, упорядоченный по дате продажи.
- Определение последних заказов для каждого клиента:
SELECT customer_id, order_id, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) AS order_rank FROM orders;Использование
order_rank = 1позволяет сразу выделить последний заказ каждого клиента.
Рекомендации по использованию:
- Всегда определяйте
ORDER BYвнутриOVER, иначе нумерация будет непредсказуемой. - Выбирайте столбцы для
PARTITION BY, которые логически делят данные на группы. - Для сложных аналитических задач комбинируйте с фильтрацией по присвоенному номеру строки, чтобы извлекать топ-N записей в каждой группе.
- Сравнивайте
ROW_NUMBER()сRANK()иDENSE_RANK(), если необходимо учитывать дубли по порядку.
Использование PARTITION BY упрощает выделение подмножеств данных, позволяя работать с каждой группой независимо, не создавая дополнительных подзапросов или соединений.
Сортировка строк перед нумерацией
Перед присвоением номеров строк важно задать точный порядок сортировки. В SQL это реализуется через ORDER BY внутри оконной функции ROW_NUMBER(). Без явной сортировки результат нумерации будет непредсказуемым.
Пример для таблицы employees с полями department_id и salary:
SELECT employee_id, department_id, salary, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num FROM employees;
Здесь строки сортируются по зарплате в пределах каждого отдела, и номер отражает позицию сотрудника по убыванию зарплаты.
Если необходимо учитывать несколько критериев, ORDER BY допускает перечисление нескольких колонок, например:
ROW_NUMBER() OVER (ORDER BY last_name ASC, first_name ASC)
Этот подход гарантирует стабильную нумерацию при одинаковых значениях первичного критерия.
При сортировке стоит учитывать производительность. Использование индексов на колонках, указанных в ORDER BY, ускоряет выполнение оконной функции. Для больших таблиц рекомендуют предварительно фильтровать данные через WHERE или CTE, чтобы сократить объем сортируемого набора.
Для динамической сортировки можно применять параметры в запросе, подставляя их в ORDER BY. Это позволяет менять критерий нумерации без переписывания всего запроса.
Важный нюанс: ROW_NUMBER() не делает сортировку постоянной в таблице. Она лишь присваивает номера на этапе выполнения запроса. Для фиксированного порядка в таблице необходимо использовать CREATE INDEX или сортировать результаты при каждом обращении.
Нумерация строк в подзапросах
В SQL подзапросы часто используют для фильтрации, агрегации или вычислений на промежуточных результатах. Для присвоения номеров строк внутри подзапроса применяют оконные функции ROW_NUMBER(), RANK() и DENSE_RANK().
Пример нумерации строк внутри подзапроса:
SELECT * FROM (
SELECT employee_id, department_id, salary,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rn
FROM employees
) AS subquery
WHERE rn <= 3;
В этом примере каждой строке в подзапросе присваивается уникальный номер внутри каждой группы department_id. Внешний запрос фильтрует только первые три зарплаты по каждому отделу.
Важно учитывать порядок сортировки внутри OVER(), так как от него зависит присвоение номера. ROW_NUMBER() назначает уникальный номер каждой строке, RANK() оставляет одинаковый номер при совпадении значений, а DENSE_RANK() не пропускает номера при одинаковых значениях.
Подзапрос с нумерацией строк можно использовать совместно с JOIN или EXISTS для выборки топ-N записей по группе, без изменения исходной таблицы. Для больших таблиц рекомендуется использовать индексы на колонках, участвующих в ORDER BY и PARTITION BY, чтобы снизить нагрузку при вычислении номеров.
При работе с агрегированными подзапросами нумерация строк позволяет отслеживать позицию записи относительно группы и применять дополнительные фильтры или аналитические вычисления без дублирования данных в основной таблице.
Получение номера строки для конкретного значения
Чтобы определить позицию конкретного значения в результирующем наборе, используется функция ROW_NUMBER() вместе с фильтром по интересующему столбцу. Например, в таблице employees с колонками id и salary можно найти номер строки для сотрудника с зарплатой 50000.
Пример запроса:
SELECT row_num
FROM (
SELECT id, salary, ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num
FROM employees
) AS numbered
WHERE salary = 50000;
В этом запросе:
| Элемент | Описание |
|---|---|
| ROW_NUMBER() OVER (ORDER BY salary DESC) | Присваивает каждой строке уникальный номер на основе убывания зарплаты. |
| Подзапрос AS numbered | Создает промежуточную таблицу с номерами строк для последующей фильтрации. |
| WHERE salary = 50000 | Отбирает строку с конкретным значением для получения её позиции. |
Если значение повторяется, и нужен номер первой встречи, используется ROW_NUMBER(), как в примере. Для всех позиций можно применить RANK() или DENSE_RANK(), которые учитывают одинаковые значения.
Пример с DENSE_RANK() для всех сотрудников с зарплатой 50000:
SELECT id, salary, dr
FROM (
SELECT id, salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS dr
FROM employees
) AS ranked
WHERE salary = 50000;
Использование оконных функций позволяет гибко находить позицию значения без изменения исходной структуры таблицы и обеспечивает точные результаты даже при дублирующихся данных.
Комбинация ROW_NUMBER() с фильтрацией
Функция ROW_NUMBER() присваивает уникальный номер каждой строке в пределах указанного окна. Чтобы использовать её для фильтрации, создают вложенный запрос с нумерацией и затем применяют условие в основном запросе.
Пример: получить 5 самых последних заказов для каждого клиента:
SELECT * FROM (
SELECT
CustomerID, OrderID, OrderDate,
ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY OrderDate DESC) AS rn
FROM Orders
) AS t
WHERE rn <= 5;
В этом примере PARTITION BY группирует строки по клиенту, ORDER BY задаёт порядок нумерации, а фильтр rn <= 5 оставляет только последние пять заказов. ROW_NUMBER() полезна при удалении дубликатов: достаточно нумеровать строки по ключевым полям и оставить только rn = 1.
При фильтрации следует учитывать производительность. Для больших таблиц использование индексов по колонкам, участвующим в ORDER BY и PARTITION BY, снижает нагрузку. Если нужна выборка по диапазону номеров, лучше применять ROW_NUMBER() в CTE (WITH) для удобства и читаемости.
Пример фильтрации диапазона: извлечь строки с 11 по 20 для конкретного продукта:
WITH RankedSales AS (
SELECT ProductID, SaleDate, Quantity,
ROW_NUMBER() OVER(ORDER BY SaleDate) AS rn
FROM Sales
WHERE ProductID = 101
)
SELECT * FROM RankedSales
WHERE rn BETWEEN 11 AND 20;
Такой подход позволяет сочетать строгую нумерацию строк с точной фильтрацией по нужным условиям без дополнительных сложных JOIN или подзапросов.
Различия нумерации строк в MySQL, PostgreSQL и SQL Server

В MySQL для нумерации строк используют функцию ROW_NUMBER() в сочетании с OVER(). Например: ROW_NUMBER() OVER (ORDER BY id) присваивает каждой строке уникальный порядковый номер, начиная с 1, согласно указанному порядку. В MySQL до версии 8.0 альтернативой была переменная пользовательского уровня, например: @row := @row + 1.
PostgreSQL поддерживает ROW_NUMBER() с полным синтаксисом оконных функций. Дополнительно возможны функции RANK() и DENSE_RANK(), которые позволяют учитывать дублирующиеся значения. Пример: ROW_NUMBER() OVER (PARTITION BY category ORDER BY created_at) присваивает номер строки внутри каждой категории.
SQL Server использует ROW_NUMBER() аналогично PostgreSQL, но часто применяется совместно с PARTITION BY для групповой нумерации и ORDER BY для задания порядка. Отличие в поведении RANK() и DENSE_RANK() заключается в том, что RANK() пропускает номера при повторениях, а DENSE_RANK() нет, что важно при построении отчетов с порядковыми номерами.
При переносе запросов между СУБД важно учитывать поддержку оконных функций: MySQL до версии 8.0 не поддерживает ROW_NUMBER(), PostgreSQL всегда поддерживает, SQL Server – с 2005 года. Для корректной нумерации в разных СУБД рекомендуется явно указывать ORDER BY и проверять поведение функций с повторяющимися значениями.
Обход ограничений старых версий SQL без ROW_NUMBER()
В старых версиях SQL, где функция ROW_NUMBER() отсутствует, нумерацию строк можно реализовать с использованием переменных или подзапросов.
Пример с использованием переменной в MySQL:
SET @row_num = 0;
SELECT @row_num := @row_num + 1 AS row_number, id, name
FROM users
ORDER BY id;
Здесь переменная @row_num увеличивается на единицу для каждой строки в порядке, указанном в ORDER BY.
Для MS SQL до версии 2005 можно использовать подзапрос с COUNT:
SELECT t1.id, t1.name,
(SELECT COUNT(*)
FROM users t2
WHERE t2.id <= t1.id) AS row_number
FROM users t1
ORDER BY t1.id;
Подзапрос считает количество записей с меньшим или равным идентификатором, фактически создавая номер строки.
Использование временных таблиц или CTE без ROW_NUMBER() также возможно:
CREATE TEMPORARY TABLE temp_users AS
SELECT id, name FROM users ORDER BY id;
SET @row_num = 0;
SELECT @row_num := @row_num + 1 AS row_number, id, name
FROM temp_users;
Этот подход гарантирует корректную нумерацию даже при сложной сортировке.
Рекомендации:
- Всегда явно указывайте
ORDER BY, иначе нумерация будет непредсказуемой. - При больших таблицах подзапрос с COUNT может быть медленным; предпочтительнее использовать переменные.
- Для пересортировки данных лучше создавать временные таблицы, чтобы избежать пересчета переменной на лету.
Вопрос-ответ:
Как добавить номер строки к результату запроса в SQL?
Для присвоения номеров строк в SQL можно использовать функцию ROW_NUMBER(). Она нумерует строки в пределах указанного порядка. Например, выражение ROW_NUMBER() OVER (ORDER BY id) присвоит каждой строке уникальный номер, начиная с 1, по возрастанию поля id. Номер строки можно использовать для фильтрации или сортировки данных в дальнейшем.
В чем разница между ROW_NUMBER(), RANK() и DENSE_RANK()?
Функция ROW_NUMBER() присваивает уникальный номер каждой строке независимо от повторяющихся значений. RANK() также нумерует строки, но одинаковые значения получают одинаковый ранг, при этом пропускаются следующие номера. DENSE_RANK() работает похоже на RANK(), но пропусков в нумерации нет — после одинаковых значений сразу идёт следующий номер. Выбор зависит от того, как вы хотите учитывать дубликаты при нумерации.
Можно ли определить номер строки без указания порядка сортировки?
Нет, функция ROW_NUMBER() требует использования ORDER BY внутри OVER(). Без указания порядка SQL не сможет присвоить строки осмысленной нумерации, так как порядок вывода данных по умолчанию не гарантирован. Если порядок не важен, можно использовать любое поле для сортировки, но для стабильного результата лучше выбрать уникальное поле.
Как ограничить вывод только определённой строки по её номеру?
Сначала необходимо пронумеровать строки с помощью ROW_NUMBER(), а затем использовать этот номер в условии WHERE. Например, чтобы получить третью строку, можно сделать так: WITH numbered AS (SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn FROM table_name) SELECT * FROM numbered WHERE rn = 3;. Такой подход позволяет получать конкретную строку без изменения исходных данных.
Есть ли различия в использовании ROW_NUMBER() между разными СУБД?
Большинство современных СУБД поддерживают ROW_NUMBER(), но синтаксис может немного отличаться. В PostgreSQL, SQL Server и Oracle функция работает одинаково. В MySQL поддержка появилась с версии 8.0. В более старых версиях MySQL приходилось использовать пользовательские переменные для нумерации строк, что было менее удобно и требовало дополнительной логики.
