К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как пронумеровать записи в запросе 1С

Как пронумеровать записи в запросе 1С: нумерация через временные таблицы, СКД, обход выборки и функцию АВТОНОМЕРЗАПИСИ. Примеры кода, ограничения и типичные ошибки.

Нумерация строк результата запроса — частая задача при построении отчётов, выгрузках в табличные документы и подготовке данных для обмена. В языке запросов 1С нет прямого аналога функции ROW_NUMBER() из классического SQL, поэтому разработчику приходится выбирать один из обходных приёмов: нумерацию при обходе выборки, вычисляемые поля СКД, соединение таблицы с самой собой или использование специальных механизмов платформы.

Выбор способа зависит от того, где будет использоваться результат. Если данные выводятся в отчёт на СКД, правильнее нумеровать строки средствами самой СКД — это быстрее и не требует доработки запроса. Если результат обрабатывается программно, проще добавить счётчик при обходе выборки. Нумерация непосредственно в тексте запроса нужна реже всего, но иногда без неё не обойтись — например, когда номер должен участвовать в последующих соединениях или группировках.

Ниже разобраны рабочие приёмы с примерами кода, их ограничения и критерии выбора. Все примеры применимы к управляемым и обычным приложениям на актуальных версиях платформы 1С:Предприятие 8.3.

Нумерация при обходе выборки — самый простой способ

Если результат запроса обрабатывается в коде — выгружается в табличный документ, таблицу значений или передаётся в обмен — номер строки логично формировать обычным счётчиком. Это самый быстрый и предсказуемый вариант: он не усложняет запрос и не нагружает СУБД дополнительными соединениями.

Порядок действий: выполнить запрос с нужным упорядочиванием, получить выборку, завести переменную-счётчик и увеличивать её на каждой итерации цикла. Номер можно записывать в отдельную колонку таблицы значений или сразу выводить в табличный документ.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Товар,

| Номенклатура.Код КАК Код

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ГДЕ

| НЕ Номенклатура.ПометкаУдаления

|

|УПОРЯДОЧИТЬ ПО

| Номенклатура.Наименование";

ТаблицаРезультат = Запрос.Выполнить().Выгрузить();

ТаблицаРезультат.Колонки.Добавить("НомерСтроки");

НомерСтроки = 0;

Для Каждого Строка Из ТаблицаРезультат Цикл

НомерСтроки = НомерСтроки + 1;

Строка.НомерСтроки = НомерСтроки;

КонецЦикла;

Ключевое условие — в запросе обязательно должно быть предложение УПОРЯДОЧИТЬ ПО. Без явной сортировки порядок строк не гарантирован, и нумерация окажется случайной при каждом выполнении. Если нумерация нужна в разрезе групп (например, отдельно по каждому складу), счётчик сбрасывают при смене значения группировочного поля.

Нумерация строк в отчёте на СКД

Когда нумерованный список нужен в отчёте, оптимальное решение — вычисляемое поле с системной функцией нумерации. СКД выполняет расчёт на этапе формирования отчёта, запрос остаётся простым, а номер автоматически учитывает итоговую сортировку и группировки.

Вычисляемое поле с НомерПоПорядку

В схеме компоновки данных на закладке «Вычисляемые поля» добавьте поле, например «НомерСтроки», и в колонке «Выражение» укажите:

НомерПоПорядку(1)

Затем добавьте это поле в выбранные поля отчёта первой колонкой. Функция возвращает порядковый номер записи в текущей группировке. При наличии нескольких группировок нумерация ведётся внутри каждой — это удобно для отчётов вида «по каждому контрагенту свои позиции с 1 по N».

Нумерация в ресурсах и макетах

Если отчёт построен на произвольном макете СКД, номер строки выводят через параметр макета, значение которого заполняется тем же вычисляемым полем. Для сложных случаев — когда номер должен зависеть от отбора или вычисляться по формуле — используют функцию ВычислитьВыражение с указанием группировки.

Порядок действий для СКД
  1. 1
    Откройте схему компоновки данных, закладка «Вычисляемые поля»
  2. 2
    Добавьте поле НомерСтроки с выражением НомерПоПорядку(1)
  3. 3
    Добавьте поле первым в список выбранных полей на закладке «Настройки»
  4. 4
    Сформируйте отчёт и проверьте нумерацию внутри каждой группировки

Нумерация непосредственно в тексте запроса

Иногда номер должен быть частью самого запроса: например, чтобы отобрать первые N записей по каждой группе или соединить две таблицы по порядковому номеру. В языке запросов 1С для этого применяют приём с самосоединением: каждую запись соединяют со всеми записями, чьё ключевое поле меньше или равно текущему, и считают их количество.

ВЫБРАТЬ

Товары.Товар КАК Товар,

КОЛИЧЕСТВО(ТоварыНомера.Товар) КАК НомерСтроки

ИЗ

ВТ_Товары КАК Товары

ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Товары КАК ТоварыНомера

ПО Товары.Код >= ТоварыНомера.Код

СГРУППИРОВАТЬ ПО

Товары.Товар

УПОРЯДОЧИТЬ ПО

НомерСтроки

Здесь поле Код должно быть уникальным — иначе несколько записей получат одинаковый номер, а суммарное количество строк в нумерации превысит реальное. Если уникального поля нет, его создают искусственно: например, через комбинацию полей или через АВТОНОМЕРЗАПИСИ в предыдущем пакете запроса.

Главный недостаток приёма — производительность. Соединение «каждый с каждым» даёт квадратичный рост числа сравнений: для таблицы в 10 000 строк СУБД обработает десятки миллионов комбинаций. На больших выборках такой запрос выполняется неприемлемо долго, поэтому самосоединение оправдано только для небольших временных таблиц (условно — до нескольких тысяч строк).

Функция АВТОНОМЕРЗАПИСИ: что она умеет и чего не умеет

В языке запросов есть встроенная функция АВТОНОМЕРЗАПИСИ, которая возвращает уникальный номер записи в выборке. Её часто ошибочно принимают за готовое решение для нумерации строк. На практике у функции другое назначение: она гарантирует уникальность, но не последовательность.

ВЫБРАТЬ

АВТОНОМЕРЗАПИСИ() КАК НомерЗаписи,

Товары.Товар КАК Товар

ИЗ

ВТ_Товары КАК Товары

Номера, которые вернёт функция, могут идти с пропусками и не обязаны начинаться с единицы — конкретные значения зависят от плана выполнения запроса. Поэтому АВТОНОМЕРЗАПИСИ подходит для двух задач: создания искусственного уникального ключа (например, для последующей нумерации самосоединением) и детерминированного упорядочивания записей, когда обычной сортировки недостаточно.

Для показа пользователю сквозной нумерации 1, 2, 3… функция не подходит. Если отчёт покажет строки с номерами 4, 17, 23, это вызовет вопросы, хотя технически запрос отработал корректно.

Сравнение способов нумерации

Счётчик при обходе выборки

Подходит для программной обработки и выгрузок. Быстро, просто, но номер существует только в коде, а не в запросе.

Вычисляемое поле СКД НомерПоПорядку

Оптимально для отчётов. Учитывает группировки и сортировку автоматически. Не работает вне СКД.

Самосоединение в запросе

Даёт номер внутри запроса для дальнейших соединений. Медленно на больших таблицах, требует уникального ключа.

АВТОНОМЕРЗАПИСИ

Даёт уникальный, но непоследовательный номер. Подходит для искусственных ключей, не для показа пользователю.

Нумерация с разбивкой по группам

Задача «пронумеровать записи внутри каждой группы» встречается в отчётах по подразделениям, контрагентам, периодам. Для каждого способа нумерации она решается по-разному.

При обходе выборки используют обход по группировкам: метод Выбрать с параметром ОбходРезультатаЗапроса.ПоГруппировкам, затем вложенный цикл по детальным записям с собственным счётчиком, который обнуляется при переходе к следующей группе. Альтернатива — сравнивать значение группировочного поля с предыдущим и сбрасывать счётчик при изменении.

В СКД групповая нумерация получается автоматически: НомерПоПорядку(1) считает записи внутри текущей группировки. Если нужна сквозная нумерация через все группы, в выражении указывают более высокий уровень группировки либо выносят поле на уровень общих итогов.

В запросе с самосоединением условие соединения дополняют равенством группировочного поля: Товары.Склад = ТоварыНомера.Склад И Товары.Код >= ТоварыНомера.Код. Это ещё сильнее увеличивает стоимость запроса, поэтому для групповой нумерации предпочтительнее код или СКД.

bsl
ВыборкаГруппы = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаГруппы.Следующий() Цикл
	Номер = 0;
	ВыборкаДетали = ВыборкаГруппы.Выбрать();
	Пока ВыборкаДетали.Следующий() Цикл
		Номер = Номер + 1;
	КонецЦикла;
КонецЦикла;

Проверка результата и типичные ошибки

После внедрения нумерации результат проверяют на контрольном наборе данных: номера должны начинаться с единицы, идти без пропусков и соответствовать видимой сортировке. Для групповой нумерации дополнительно проверяют, что счётчик сбрасывается на каждой новой группе, а при пустой группе отчёт не выводит «висячий» номер.

Отдельно стоит проверить поведение при дублях ключевого поля. Если нумерация строится через самосоединение по неуникальному полю, две записи с одинаковым значением получат один номер, а итоговое количество пронумерованных строк превысит размер выборки. Признак проблемы — последний номер больше числа строк в результате.

Контрольные точки

Как проверить результат

Первый номер в каждой группе
Норма

Равен 1

Тревожный признак

Начинается с произвольного числа — вероятно, использована АВТОНОМЕРЗАПИСИ

Последний номер
Норма

Равен количеству строк в выборке

Тревожный признак

Больше числа строк — дубли ключевого поля при самосоединении

Порядок номеров
Норма

Совпадает с сортировкой, видимой пользователю

Тревожный признак

Номера идут не по порядку сортировки — в запросе нет УПОРЯДОЧИТЬ ПО

Типичные ошибки
01
Использование АВТОНОМЕРЗАПИСИ для показа пользователю — номера идут с пропусками и выглядят как ошибка отчёта
02
Нумерация без УПОРЯДОЧИТЬ ПО — при каждом выполнении строки получают разные номера
03
Самосоединение по неуникальному полю — дубли получают одинаковые номера, а последний номер превышает число строк
04
Самосоединение на большой таблице — запрос работает минутами и грузит сервер СУБД
05
Нумерация в запросе там, где отчёт строится на СКД, — лишняя сложность, когда вычисляемое поле решает задачу без изменения запроса

Как выбрать способ нумерации

Критерий выбора — место использования номера. Если номер нужен только визуально в отчёте — вычисляемое поле СКД. Если результат обрабатывается кодом — счётчик в цикле. Если номер участвует в логике запроса (соединения, отбор первых N записей группы) — самосоединение по уникальному ключу на небольшой временной таблице. АВТОНОМЕРЗАПИСИ оставьте для генерации уникальных ключей.

Маршрут решения

Как выбрать действие

Номер нужен только в отчёте на СКДДобавьте вычисляемое поле НомерПоПорядку(1)
Результат обрабатывается в коде или выгружаетсяНумеруйте счётчиком при обходе выборки
Номер нужен внутри запроса для соединенийИспользуйте самосоединение по уникальному полю на небольшой временной таблице
Нужен уникальный ключ без последовательностиПримените АВТОНОМЕРЗАПИСИ
Если нумерация нужна для отбора «первых N записей каждой группы», часто проще выполнить два запроса: сначала получить группы, затем для каждой — выборку с ПЕРВЫЕ N. Это почти всегда быстрее самосоединения и проще в сопровождении.
ПЗ
Практическое замечание

Частые вопросы

Можно ли пронумеровать записи запроса без временных таблиц и соединений?

Да, если нумерация нужна вне запроса. При обходе выборки счётчиком или через вычисляемое поле СКД временные таблицы не требуются. Внутри самого запроса без соединения или АВТОНОМЕРЗАПИСИ последовательную нумерацию получить нельзя — язык запросов 1С не имеет оконных функций.

Почему АВТОНОМЕРЗАПИСИ выдаёт номера с пропусками?

Функция гарантирует только уникальность номера в выборке, а не его последовательность. Конкретные значения зависят от того, как СУБД выполняет запрос. Это нормальное поведение, а не ошибка — для сквозной нумерации используйте другие способы.

Как пронумеровать строки динамического списка?

Динамический список не поддерживает добавление произвольных вычисляемых колонок с номером строки. Обходной путь — выводить список через отчёт на СКД с полем НомерПоПорядку либо отказаться от колонки номера, поскольку при фильтрации и прокрутке динамического списка такой номер всё равно потерял бы смысл.

Что делать, если нумерация самосоединением работает очень медленно?

Сократите объём временной таблицы отборами перед нумерацией, убедитесь, что ключевое поле проиндексировано (поместите таблицу с конструкцией ИНДЕКСИРОВАТЬ ПО). Если это не помогло — перенесите нумерацию в код: обход выборки со счётчиком выполняется за линейное время.

Как сделать нумерацию с двух уровней, например 1.1, 1.2, 2.1?

В СКД комбинируют два вычисляемых поля: номер родительской группировки и номер внутри неё, затем объединяют их в строковое выражение. В коде ведут два счётчика — внешний сбрасывает внутренний. В чистом запросе такая иерархическая нумерация потребует двух самосоединений и обычно неоправданно сложна.

Нужно ли индексировать временную таблицу для нумерации самосоединением?

Да, это заметно ускоряет запрос. При помещении таблицы во временные добавьте предложение ИНДЕКСИРОВАТЬ ПО с полем, по которому выполняется соединение. Без индекса СУБД сканирует таблицу полностью для каждой строки.

Итог

Что важно запомнить

Прямой нумерации строк в запросах 1С нет. Для отчётов используйте вычисляемое поле СКД НомерПоПорядку(1), для программной обработки — счётчик при обходе выборки, для номера внутри запроса — самосоединение по уникальному полю на небольших таблицах. АВТОНОМЕРЗАПИСИ даёт уникальные, но не последовательные номера и не подходит для показа пользователю.