Как добавить номер строки в запрос или таблицу значений в 1С:Предприятие 8?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов в «1С:Предприятие 8» программисты часто сталкиваются с задачей вывода пронумерованного списка данных на управляемую форму. Если данные берутся из табличной части документа или справочника, платформа предоставляет готовое системное поле НомерСтроки. Однако, если источником данных служит Регистр сведений, в нем изначально отсутствует последовательная нумерация записей. Когда этот результат запроса выгружается в таблицу значений на форме, разработчику приходится самостоятельно искать способы отображения номеров строк.

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

Вариант 1. Постобработка таблицы значений в цикле (Рекомендуемый метод)

Рассмотрим самый надежный, производительный и простой в реализации способ. Большинство ведущих разработчиков сходятся во мнении, что нумеровать строки непосредственно в запросе СУБД ради простого вывода на форму нецелесообразно. Гораздо эффективнее произвести быструю постобработку полученной таблицы значений в оперативной памяти сервера приложений перед передачей её на клиент.

Разберем этот процесс по шагам:

  1. Выполняем запрос к регистру сведений и выгружаем результат в промежуточную таблицу значений.
  2. Добавляем в созданную таблицу новую колонку для номеров строк с типом данных Число.
  3. Запускаем цикл обхода строк таблицы значений, в котором последовательно записываем индекс строки (увеличенный на единицу) в созданную колонку.
  4. Передаем готовую таблицу значений в реквизит управляемой формы с помощью стандартной процедуры платформы.

Посмотрим на пример программного кода, реализующего этот алгоритм:


// 1. Выполняем запрос и выгружаем результат в Таблицу Значений (ТЗ)
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	КурсыВалют.Период КАК Период,
    |	КурсыВалют.Валюта КАК Валюта,
    |	КурсыВалют.Курс КАК Курс
    |ИЗ
    |	РегистрСведений.КурсыВалют КАК КурсыВалют";

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

// 2. Добавляем колонку для нумерации
ТЗ.Колонки.Добавить("НомерСтроки", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0)));

// 3. Заполняем номера строк в цикле
Для Счетчик = 0 По ТЗ.Количество() - 1 Цикл
    ТЗ[Счетчик].НомерСтроки = Счетчик + 1;
КонецЦикла;

// 4. Загружаем подготовленные данные в реквизит управляемой формы
ЗначениеВРеквизитФормы(ТЗ, "РеквизитНаФорме");

Преимущества данного метода:

Вариант 2. Использование встроенной функции АВТОНОМЕРЗАПИСИ() (Начиная с платформы 8.3.13)

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

Выясним особенности применения этой функции. Главное правило — её нельзя использовать непосредственно в финальном выводе запроса. Она работает исключительно при помещении результатов во временную таблицу.

Проанализируем пример структуры такого запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	АВТОНОМЕРЗАПИСИ() КАК НомерСтроки,
    |	КурсыВалют.Период КАК Период,
    |	КурсыВалют.Валюта КАК Валюта,
    |	КурсыВалют.Курс КАК Курс
    |ПОМЕСТИТЬ ВТ_ПронумерованныеДанные
    |ИЗ
    |	РегистрСведений.КурсыВалют КАК КурсыВалют
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ_ПронумерованныеДанные.НомерСтроки КАК НомерСтроки,
    |	ВТ_ПронумерованныеДанные.Период КАК Период,
    |	ВТ_ПронумерованныеДанные.Валюта КАК Валюта,
    |	ВТ_ПронумерованныеДанные.Курс КАК Курс
    |ИЗ
    |	ВТ_ПронумерованныеДанные КАК ВТ_ПронумерованныеДанные";

ТЗ = Запрос.Выполнить().Выгрузить();
ЗначениеВРеквизитФормы(ТЗ, "РеквизитНаФорме");

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

Вариант 3. Имитация оконных функций через левое соединение в запросе

В классическом SQL для подобных целей применяются оконные функции вроде ROW_NUMBER() OVER (ORDER BY ...). В языке запросов 1С такой механизм отсутствует напрямую. Тем не менее, решить задачу в рамках одного запроса (без использования новых функций платформы) можно путем левого соединения исходной таблицы с самой собой по условию «больше» или «меньше».

Рассмотрим пример реализации такой логики для справочника или таблицы с уникальными идентификаторами:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ИсходныеДанные
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Родитель В ИЕРАРХИИ(&Группа)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ИсходнаяТаблица.Ссылка КАК Ссылка,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ТаблицаСравнения.Ссылка) + 1 КАК НомерСтроки
ИЗ
    ВТ_ИсходныеДанные КАК ИсходнаяТаблица
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИсходныеДанные КАК ТаблицаСравнения
        ПО ИсходнаяТаблица.Ссылка > ТаблицаСравнения.Ссылка
СГРУППИРОВАТЬ ПО
    ИсходнаяТаблица.Ссылка
УПОРЯДОЧИТЬ ПО
    НомерСтроки

Почему этот способ опасен для практического применения?

Если мы работаем с Регистром сведений, у нас нет уникального поля Ссылка. Нам придется связывать таблицы по набору измерений, что сильно усложнит условие связи. Но самая большая проблема заключается в производительности СУБД.

При левом соединении таблицы «сама на себя» по неравенству СУБД строит декартово произведение строк. Вычислительная сложность такого запроса оценивается как $O(N^2)$. Если ваша таблица содержит всего 100 строк, СУБД обработает около 10 000 сопоставлений. Если же в таблице окажется 10 000 строк, серверу придется проанализировать до 50 миллионов записей. Это гарантированно перегрузит оперативную память сервера и приведет к зависанию сеанса пользователя.

Вариант 4. Особенности работы с идентификаторами строк управляемой формы

Иногда у начинающих разработчиков возникает соблазн использовать системное свойство элементов коллекции формы. При загрузке таблицы значений на управляемую форму, она преобразуется в объект ДанныеФормыКоллекция. Каждая строка этой коллекции имеет метод ПолучитьИдентификатор().

Важно понимать, почему нельзя использовать идентификатор в качестве номера строки:

Резюме и рекомендации

Подводя итог нашему анализу, сформулируем основные правила выбора метода нумерации строк:

  1. Для решения стандартных задач вывода пронумерованного списка на форму документа или обработки всегда используйте Вариант 1 (обход таблицы значений в цикле на сервере). Это самый безопасный, быстрый и легко читаемый способ, который гарантирует стабильность работы системы независимо от объема данных.
  2. Избегайте использования сложных левых соединений для нумерации в запросах СУБД. Это создает колоссальную нагрузку на сервер баз данных.
  3. Если вы выводите отчетные данные, рассмотрите возможность перевода задачи на систему компоновки данных (СКД), где присутствует стандартное системное поле СистемныеПоля.НомерПоПорядку.
← На главную