При работе с результатами запросов в среде «1С:Предприятие» начинающие разработчики часто сталкиваются с проблемой: как быстро «прыгнуть» на определенную строку (например, на сотую или тысячную), не перебирая все предыдущие записи. Типичная ошибка заключается в попытке использовать метод Получить() непосредственно у объекта выборки, что приводит к исключению «Значение индекса выходит за границы диапазона».
В этой статье мы подробно разберем, почему стандартные методы могут не работать так, как ожидается, и проанализируем несколько эффективных способов решения этой задачи в зависимости от объема данных и требований к производительности.
Рассмотрим ситуацию, с которой столкнулся автор темы. При выполнении кода ВыборкаДетальныеЗаписи.Получить(100) система выдает ошибку, даже если в результате запроса тысячи строк. Выясним причину: у объекта ВыборкаИзРезультатаЗапроса метод Получить(Индекс) предназначен для получения значения поля (колонки) текущей записи, а не самой строки. Если в вашем запросе выбрано 4 поля, то допустимые индексы — от 0 до 3. Попытка обратиться к индексу 100 воспринимается системой как обращение к 101-й колонке, которой не существует.
Для того чтобы свободно перемещаться по строкам по их номеру, необходимо использовать другие подходы.
Самый простой и интуитивно понятный способ — преобразовать динамическую выборку в статичную таблицу значений. Это позволяет обращаться к любой строке по индексу, используя квадратные скобки.
Рассмотрим пример реализации:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ИнвентарныеНомераОС.Ссылка КАК Ссылка,
| ИнвентарныеНомераОС.Код КАК Код
|ИЗ
| Справочник.ИнвентарныеНомераОС КАК ИнвентарныеНомераОС";
Результат = Запрос.Выполнить();
// Вместо Результат.Выбрать() используем Выгрузить()
ТаблицаРезультатов = Результат.Выгрузить();
// Теперь мы можем обратиться к 100-й строке (индекс 99)
Если ТаблицаРезультатов.Количество() >= 100 Тогда
НужнаяСтрока = ТаблицаРезультатов[99];
Сообщить("Код 100-й записи: " + НужнаяСтрока.Код);
КонецЕсли;
Важное замечание: Метод Выгрузить() копирует все данные из временного хранилища в оперативную память сервера. Если запрос возвращает сотни тысяч строк, это может привести к избыточному потреблению ресурсов. Используйте этот метод только для небольших или средних объемов данных.
Если нам важно сохранить экономию памяти, мы можем использовать стандартный объект ВыборкаИзРезультатаЗапроса. Поскольку выборка — это «курсор», который двигается только вперед, нам придется пропустить ненужные строки с помощью цикла.
Разберем, как это сделать правильно:
Выборка = Результат.Выбрать();
НомерНужнойСтроки = 100;
ТекущийНомер = 0;
Пока Выборка.Следующий() Цикл
ТекущийНомер = ТекущийНомер + 1;
Если ТекущийНомер = НомерНужнойСтроки Тогда
// Мы встали на нужную позицию
Сообщить("Мы на 100-й строке: " + Выборка.ИнвНомер);
Прервать;
КонецЕсли;
КонецЦикла;
Этот метод работает медленнее, чем прямое обращение по индексу в таблице, но он практически не потребляет оперативную память, так как данные читаются порциями.
Если целью является не просто «строка номер 100», а строка с определенным значением (например, конкретным инвентарным номером), то оптимально использовать метод НайтиСледующий(). Посмотрим на пример:
ПараметрыПоиска = Новый Структура;
ПараметрыПоиска.Вставить("Код", "000000123");
Выборка = Результат.Выбрать();
Если Выборка.НайтиСледующий(ПараметрыПоиска) Тогда
// Выборка автоматически позиционируется на найденной строке
Сообщить("Запись найдена!");
КонецЕсли;
Проанализируем современные возможности языка запросов. Начиная с версии 8.3.20, разработчикам доступна функция АВТОНОМЕРЗАПИСИ(). Это позволяет пронумеровать строки прямо в момент выполнения запроса в базе данных.
Зачем это нужно? Мы можем обернуть основной запрос во вложенный и наложить условие ГДЕ на поле с номером строки. Это самый производительный способ, так как сервер 1С получит от SQL-сервера только одну конкретную строку, а не всю выборку.
Выгрузить() и обращение через квадратные скобки Таблица[Индекс]. Это дает самый чистый и понятный код.Следующий() или постраничную выборку через конструкцию ВЫБРАТЬ ПЕРВЫЕ... в самом тексте запроса.Выборка.Сбросить(). Это вернет указатель в позицию «перед первой строкой», и вы сможете начать отсчет заново без повторного обращения к базе данных.НайтиСледующий(), если у вас есть уникальный идентификатор строки.Таким образом, выбор метода позиционирования напрямую зависит от контекста задачи. Понимание разницы между получением значения колонки и переходом к строке поможет вам избежать досадных ошибок и писать более оптимизированный код.