Как в 1С обойти таблицу значений порциями по 50 элементов?

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

При работе с большими объемами данных в 1С часто возникает необходимость обрабатывать ТаблицуЗначений не целиком, а частями. Это может быть связано с требованиями внешних систем (например, лимиты REST OData), необходимостью разделения транзакций для снижения блокировок или просто для оптимизации потребления оперативной памяти. В этой статье мы разберем несколько эффективных способов реализации такой задачи, проанализируем возможные ошибки и выберем наиболее производительные подходы.

Метод 1: Использование циклов с индексами (оптимальный вариант)

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

Рассмотрим код, который учитывает нюансы индексации в 1С (индексы начинаются с 0) и корректно обрабатывает последнюю порцию, даже если она меньше 50 элементов:


Индекс = 0;
РазмерПорции = 50;
ВсегоСтрок = ТЗ.Количество();

Пока Индекс < ВсегоСтрок Цикл
    // Вычисляем конечный индекс текущей порции
    КонечныйИндекс = Мин(Индекс + РазмерПорции - 1, ВсегоСтрок - 1);
    
    // Передаем порцию в процедуру обработки
    ОбработатьПорциюДанных(ТЗ, Индекс, КонечныйИндекс);
    
    // Переходим к следующему блоку
    Индекс = Индекс + РазмерПорции;
КонецЦикла;

Процедура ОбработатьПорциюДанных(ТЗ, Начало, Конец)
    Для ТекИндекс = Начало По Конец Цикл
        СтрокаТЗ = ТЗ[ТекИндекс];
        // Здесь выполняется основная логика обработки строки
    КонецЦикла;
КонецПроцедуры

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

Метод 2: Счетчик и деление по модулю

Этот способ часто выбирают для простых задач внутри стандартного обхода Для Каждого. Проанализируем его реализацию:


Счетчик = 0;
Для Каждого СтрокаТЗ Из ТЗ Цикл
    Счетчик = Счетчик + 1;
    
    // Добавляем строку в текущий контекст обработки (например, массив или временную ТЗ)
    // ... логика накопления ...

    Если Счетчик % 50 = 0 Тогда
        // Порция сформирована, выполняем действие (например, фиксацию транзакции)
        ЗафиксироватьПорцию();
    КонецЕсли;
КонецЦикла;

// Важно не забыть обработать остаток, который не кратен 50
Если Счетчик % 50 <> 0 Тогда
    ЗафиксироватьПорцию();
КонецЕсли;

Выясним причину, по которой этот метод может быть менее удобен: он требует обязательного дублирования кода обработки (или вызова одной и той же процедуры) после завершения основного цикла, чтобы обработать "хвост" данных.

Метод 3: Копирование порций в массив строк (высокая производительность)

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

Рассмотрим алгоритм действий:

  1. Создаем массив для накопления строк.
  2. В цикле добавляем ссылки на строки ТЗ в массив.
  3. По достижении 50 строк вызываем ТЗ.Скопировать(МассивСтрок).
  4. Очищаем массив и повторяем процесс.

МассивСтрок = Новый Массив;
Для Каждого СтрокаТЗ Из ТЗ Цикл
    МассивСтрок.Добавить(СтрокаТЗ);
    
    Если МассивСтрок.Количество() = 50 Тогда
        ПорцияТЗ = ТЗ.Скопировать(МассивСтрок);
        ОбработатьГотовуюТаблицу(ПорцияТЗ);
        МассивСтрок.Очистить();
    КонецЕсли;
КонецЦикла;

Если МассивСтрок.Количество() > 0 Тогда
    ПорцияТЗ = ТЗ.Скопировать(МассивСтрок);
    ОбработатьГотовуюТаблицу(ПорцияТЗ);
КонецЕсли;

Этот подход значительно быстрее ручного создания строк через Добавить() и ЗаполнитьЗначенияСвойств(), так как копирование выполняется на уровне внутренних механизмов платформы.

Особенности работы с REST OData и внешними запросами

Если вы столкнулись с необходимостью порционной обработки из-за ограничений REST-запросов (как упоминалось в обсуждении), важно помнить о параметрах $top и $skip. Рассмотрим ситуацию: вместо того чтобы выгружать огромную таблицу в память 1С и потом "резать" её на куски, правильнее запрашивать данные у источника сразу порциями.

Если же данные уже в ТаблицеЗначений, и вы формируете параметры для GET-запроса, используйте первый метод с индексами для сборки строки запроса. Это минимизирует накладные расходы на создание объектов.

Пагинация на уровне СУБД (запросы)

Проанализируем ситуацию, когда данные еще не в таблице, а в базе данных. Использование конструкции ВЫБРАТЬ ПЕРВЫЕ 50 совместно с условием по уникальному идентификатору (например, ГДЕ Ссылка > &ПоследняяСсылка) — это "золотой стандарт" работы с большими выборками. Это гораздо эффективнее, чем выгрузка всей базы в ТаблицуЗначений на сервере 1С с последующим делением.

Рекомендации по оптимизации памяти

При работе с экстремально большими таблицами (миллионы строк) даже простой обход может вызвать переполнение памяти. В таких случаях мы рекомендуем рассмотреть стратегию "обработки с удалением":

  1. В цикле всегда берем первые 50 строк таблицы (индексы 0–49).
  2. Обрабатываем их.
  3. Удаляем обработанные строки из исходной таблицы методом Удалить().
  4. Повторяем, пока ТЗ.Количество() > 0.

Внимание: Удаление строк из таблицы — операция тяжелая, поэтому этот метод стоит использовать только тогда, когда критически важно освобождать память в процессе работы алгоритма.

Подводя итог, можно сказать, что выбор метода зависит от контекста. Для простых вычислений идеально подходит Метод 1 (Индексы). Если нужно создавать подтаблицы — используйте Метод 3 (Скопировать с массивом строк). А при работе с базой данных всегда отдавайте предпочтение порционной выборке на уровне запроса.

← На главную