Как получить номер текущей строки при обходе выборки из результата запроса в 1С?

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

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

Особенности работы объекта «ВыборкаИзРезультатаЗапроса»

Проанализируем архитектуру работы с результатами запросов на платформе «1С:Предприятие». Объект ВыборкаИзРезультатаЗапроса представляет собой последовательный однонаправленный курсор (итератор). Его ключевые особенности:

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

Способ 1. Использование переменной-счетчика (рекомендуемый вариант)

Рассмотрим базовый и наиболее производительный способ определения номера текущей строки при работе с объектом ВыборкаИзРезультатаЗапроса — инициализацию явной переменной-счетчика перед циклом.

Посмотрим на пример правильной организации цикла:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.Наименование КАК Наименование
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура";

Выборка = Запрос.Выполнить().Выбрать();
ВсегоСтрок = Выборка.Количество();
НомерСтроки = 0;

Пока Выборка.Следующий() Цикл
	НомерСтроки = НомерСтроки + 1;
	
	// НомерСтроки содержит точный порядковый номер текущей записи (от 1 до ВсегоСтрок)
	// Здесь выполняется основная логика обработки данных
КонецЦикла;

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

Способ 2. Выгрузка в Таблицу Значений и получение индекса

Разберем альтернативный вариант, когда результат запроса предварительно выгружается в объект ТаблицаЗначений. У строк таблицы значений есть системный метод Индекс(), возвращающий индекс строки в коллекции (начиная с нуля).

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


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

// Вариант А: Перебор по индексам
ВсегоЭлементов = ТЗ.Количество();
Для Индекс = 0 По ВсегоЭлементов - 1 Цикл
	СтрокаТЗ = ТЗ[Индекс];
	НомерСтроки = Индекс + 1;
	// Обработка строки
КонецЦикла;

// Вариант Б: Перебор коллекции с обращением к методу Индекс()
Для Каждого СтрокаТЗ Из ТЗ Цикл
	Индекс = ТЗ.Индекс(СтрокаТЗ);
	НомерСтроки = Индекс + 1;
	// Обработка строки
КонецЦикла;

Важное предупреждение о производительности: метод Запрос.Выполнить().Выгрузить() полностью считывает весь массив данных в оперативную память сервера. Если в выборке содержится 100 000 и более строк, это может привести к избыточному потреблению RAM и риску падения рабочего процесса. Кроме того, вызов ТЗ.НайтиСтроки() с последующим ТЗ.Индекс() внутри больших циклов без построения индексов через ТЗ.Индексы.Добавить() выполняет линейный поиск полным перебором, что кратно замедляет выполнение кода.

Оценка прогресса в отладчике, если цикл уже запущен без счетчика

Выясним причину, почему разработчики часто задаются этим вопросом постфактум: длительная обработка уже запущена на боевой базе, счетчик внутри кода не объявлен, а прерывать операцию нежелательно, чтобы не терять часы выполненной работы.

Если процесс остановлен точкой останова в отладчике, косвенно оценить текущий прогресс можно следующими приемами:

  1. Анализ сортируемых полей: если в тексте запроса присутствовала секция УПОРЯДОЧИТЬ ПО (например, по дате документа, коду или наименованию), в табло отладчика можно посмотреть текущее значение поля Выборка.Наименование или Выборка.Дата и сопоставить его с общим диапазоном значений в информационной базе.
  2. Сравнение с общим числом: вызовите в табло выражение Выборка.Количество(), чтобы понимать финальный объем выборки.

Правильная организация длительных циклов и индикация процесса

Чтобы избежать ситуаций, когда состояние длительной обработки остается неизвестным, рассмотрим лучшие практики платформы 1С для вывода прогресса и защиты от зависаний.

Посмотрим на пример комплексного обхода с выводом состояния пользователю и возможностью прерывания:


Выборка = Запрос.Выполнить().Выбрать();
ВсегоСтрок = Выборка.Количество();
ТекущийНомер = 0;

Пока Выборка.Следующий() Цикл
	ТекущийНомер = ТекущийНомер + 1;
	
	// Вызов проверки прерывания (по нажатию Ctrl + Break / Ctrl + C)
	ОбработкаПрерыванияПользователя();
	
	// Обновление строки состояния для клиента каждые 100 итераций
	Если ТекущийНомер % 100 = 0 Тогда
		ПроцентВыполнения = Окр((ТекущийНомер / ВсегоСтрок) * 100, 1);
		Состояние("Обработка данных...", ПроцентВыполнения, "Обработано " + Строка(ТекущийНомер) + " из " + Строка(ВсегоСтрок));
	КонецЕсли;
	
	// Логика обработки
КонецЦикла;

Для сверхбольших массивов данных на стороне сервера рекомендуется переходить от сплошной выборки к порционной (пакетной) обработке через конструкцию ВЫБРАТЬ ПЕРВЫЕ N с сортировкой и отбором по ссылке (ГДЕ Ссылка > &ПредыдущаяСсылка УПОРЯДОЧИТЬ ПО Ссылка), либо использовать механизм длительных операций с фоновыми заданиями.

← На главную