При работе с результатами запросов в 1С:Предприятии программисты часто сталкиваются с необходимостью узнать индекс текущей строки или быстро вернуться к определенной записи в объекте ВыборкаИзРезультатаЗапроса. В отличие от таблиц значений, выборка является динамическим курсором, оптимизированным для последовательного обхода данных. Рассмотрим подробнее, почему у выборки нет встроенного свойства индекса и какими способами можно решить задачу позиционирования.
Для начала проанализируем природу объекта. Когда мы выполняем РезультатЗапроса.Выбрать(), система не загружает все данные в память клиента целиком в виде индексированного массива. Вместо этого создается указатель на набор данных. Метод Следующий() перемещает этот указатель вперед. Именно поэтому стандартного свойства Индекс или НомерСтроки у выборки не существует — система «не знает», на какой по счету строке она находится, пока не проитерирует их от начала.
Если ваша логика подразумевает последовательный обход данных с помощью метода Следующий(), самым простым и производительным решением будет добавление переменной-счетчика. Разберем на примере:
Выборка = РезультатЗапроса.Выбрать();
Индекс = 0; // Инициализируем счетчик
Пока Выборка.Следующий() Цикл
// Выполняем полезные действия
Сообщить("Обрабатывается строка с индексом: " + Индекс);
// В конце цикла увеличиваем счетчик
Индекс = Индекс + 1;
КонецЦикла;
Важный нюанс: этот метод безупречно работает только при линейном обходе. Если внутри цикла вы вызываете методы, которые могут сдвигать курсор (например, вложенные выборки), за состоянием счетчика придется следить более внимательно.
Если алгоритм требует произвольного доступа к строкам (например, «прыгать» от 10-й строки к 2-й и обратно), или вам критически важно знать точный индекс строки в любой момент времени, выборку лучше заменить на Таблицу Значений. Рассмотрим ситуацию, когда мы выгружаем результат целиком:
ТЗ_Результат = РезультатЗапроса.Выгрузить();
// Теперь мы можем обращаться к строке по индексу напрямую
НужнаяСтрока = ТЗ_Результат[5];
// Или узнать индекс конкретной строки
ИндексСтроки = ТЗ_Результат.Индекс(НужнаяСтрока);
Преимущество этого метода — полный контроль над данными и возможность использования метода Найти(). Недостаток — повышенный расход оперативной памяти, так как ТаблицаЗначений полностью материализуется в памяти, в отличие от потоковой выборки.
В некоторых случаях, особенно при доработке типовых печатных форм, нам нужно «вернуться» назад или найти строку по значению поля, не прерывая обход. Поскольку метода Получить(Индекс) для произвольной выборки (с группировками) может не быть или он будет работать неэффективно, проанализируем подход со сбросом курсора:
Сбросить(), чтобы вернуть указатель выборки в начало (перед первой записью).НайтиСледующий(ПараметрыПоиска), чтобы переместить курсор к строке, удовлетворяющей условию.
ПараметрыПоиска = Новый Структура("Ссылка", НужныйДокумент);
Выборка.Сбросить(); // Возвращаемся в начало
Если Выборка.НайтиСледующий(ПараметрыПоиска) Тогда
// Теперь выборка спозиционирована на нужной строке
// Можно продолжать работу методом Следующий() от этой точки
КонецЕсли;
Рассмотрим ситуацию: вам нужно одновременно читать данные шапки и перебирать строки табличной части. Вместо того чтобы пытаться «запомнить индекс» одной выборки, вы можете создать две независимые выборки из одного и того же РезультатаЗапроса.
ВыборкаШапка = РезультатЗапроса.Выбрать();
ВыборкаДанные = РезультатЗапроса.Выбрать();
Пока ВыборкаШапка.Следующий() Цикл
// Читаем общие данные
// ...
// В это время ВыборкаДанные остается на своем месте или перемещается независимо
КонецЦикла;
Если задача получения индекса возникла из-за необходимости вывести одну и ту же область документа несколько раз (например, несколько копий Счета-фактуры для разных контрагентов), использование индексов выборки — не самый оптимальный путь. В современных конфигурациях на базе БСП (Библиотека стандартных подсистем) рекомендуется работать с ТабличнымДокументом напрямую.
Вместо повторного прохода по выборке запроса, проанализируем метод копирования уже сформированных областей:
ТабличныйДокумент.Вывести() основного табличного документа, передавая в него подготовленный ранее экземпляр.ЗадатьОбластьПечатиДокумента из состава БСП для корректной разбивки на страницы и идентификации объектов печати.Пример логики формирования нескольких копий:
// Основной документ, который пойдет на печать
ТабДокВесь = Новый ТабличныйДокумент;
// Формируем один экземпляр во временный документ
ВременныйТабДок = СформироватьОдинЭкземпляр(ДанныеВыборки);
Для Счетчик = 1 По КоличествоКопий Цикл
ТабДокВесь.Вывести(ВременныйТабДок);
// Добавляем разрыв страницы между копиями, если нужно
Если Счетчик < КоличествоКопий Тогда
ТабДокВесь.ВывестиГоризонтальныйРазделительСтраниц();
КонецЕсли;
КонецЦикла;
Выясним причину, по которой вам потребовался индекс. Если это простая нумерация строк в отчете — используйте счетчик в цикле. Если это сложная логика перехода между строками — выгружайте результат в Таблицу Значений. Если же вы работаете в рамках подсистемы печати 1С, лучше всего манипулировать областями Табличного Документа, что позволит оставить код формирования данных (запросы и выборки) максимально простым и производительным.