При работе с большими объемами данных в 1С часто возникает необходимость обрабатывать ТаблицуЗначений не целиком, а частями. Это может быть связано с требованиями внешних систем (например, лимиты REST OData), необходимостью разделения транзакций для снижения блокировок или просто для оптимизации потребления оперативной памяти. В этой статье мы разберем несколько эффективных способов реализации такой задачи, проанализируем возможные ошибки и выберем наиболее производительные подходы.
Разберем самый надежный и быстрый способ обхода таблицы. Основная идея заключается в использовании цикла Пока, где мы вручную управляем индексами строк. Это позволяет нам точно определять границы каждой порции и избегать лишнего копирования данных.
Рассмотрим код, который учитывает нюансы индексации в 1С (индексы начинаются с 0) и корректно обрабатывает последнюю порцию, даже если она меньше 50 элементов:
Индекс = 0;
РазмерПорции = 50;
ВсегоСтрок = ТЗ.Количество();
Пока Индекс < ВсегоСтрок Цикл
// Вычисляем конечный индекс текущей порции
КонечныйИндекс = Мин(Индекс + РазмерПорции - 1, ВсегоСтрок - 1);
// Передаем порцию в процедуру обработки
ОбработатьПорциюДанных(ТЗ, Индекс, КонечныйИндекс);
// Переходим к следующему блоку
Индекс = Индекс + РазмерПорции;
КонецЦикла;
Процедура ОбработатьПорциюДанных(ТЗ, Начало, Конец)
Для ТекИндекс = Начало По Конец Цикл
СтрокаТЗ = ТЗ[ТекИндекс];
// Здесь выполняется основная логика обработки строки
КонецЦикла;
КонецПроцедуры
Почему это решение эффективно: мы вызываем метод Количество() один раз (вынеся его из условия цикла), используем встроенную функцию Мин() для предотвращения выхода за границы массива и не создаем промежуточных объектов в памяти.
Этот способ часто выбирают для простых задач внутри стандартного обхода Для Каждого. Проанализируем его реализацию:
Счетчик = 0;
Для Каждого СтрокаТЗ Из ТЗ Цикл
Счетчик = Счетчик + 1;
// Добавляем строку в текущий контекст обработки (например, массив или временную ТЗ)
// ... логика накопления ...
Если Счетчик % 50 = 0 Тогда
// Порция сформирована, выполняем действие (например, фиксацию транзакции)
ЗафиксироватьПорцию();
КонецЕсли;
КонецЦикла;
// Важно не забыть обработать остаток, который не кратен 50
Если Счетчик % 50 <> 0 Тогда
ЗафиксироватьПорцию();
КонецЕсли;
Выясним причину, по которой этот метод может быть менее удобен: он требует обязательного дублирования кода обработки (или вызова одной и той же процедуры) после завершения основного цикла, чтобы обработать "хвост" данных.
Если ваша задача требует создания новой ТаблицыЗначений для каждой порции (например, для передачи в другой метод), самым быстрым способом будет использование метода Скопировать(). Платформа 1С оптимизирована для работы с массивами строк при создании новых таблиц.
Рассмотрим алгоритм действий:
ТЗ.Скопировать(МассивСтрок).
МассивСтрок = Новый Массив;
Для Каждого СтрокаТЗ Из ТЗ Цикл
МассивСтрок.Добавить(СтрокаТЗ);
Если МассивСтрок.Количество() = 50 Тогда
ПорцияТЗ = ТЗ.Скопировать(МассивСтрок);
ОбработатьГотовуюТаблицу(ПорцияТЗ);
МассивСтрок.Очистить();
КонецЕсли;
КонецЦикла;
Если МассивСтрок.Количество() > 0 Тогда
ПорцияТЗ = ТЗ.Скопировать(МассивСтрок);
ОбработатьГотовуюТаблицу(ПорцияТЗ);
КонецЕсли;
Этот подход значительно быстрее ручного создания строк через Добавить() и ЗаполнитьЗначенияСвойств(), так как копирование выполняется на уровне внутренних механизмов платформы.
Если вы столкнулись с необходимостью порционной обработки из-за ограничений REST-запросов (как упоминалось в обсуждении), важно помнить о параметрах $top и $skip. Рассмотрим ситуацию: вместо того чтобы выгружать огромную таблицу в память 1С и потом "резать" её на куски, правильнее запрашивать данные у источника сразу порциями.
Если же данные уже в ТаблицеЗначений, и вы формируете параметры для GET-запроса, используйте первый метод с индексами для сборки строки запроса. Это минимизирует накладные расходы на создание объектов.
Проанализируем ситуацию, когда данные еще не в таблице, а в базе данных. Использование конструкции ВЫБРАТЬ ПЕРВЫЕ 50 совместно с условием по уникальному идентификатору (например, ГДЕ Ссылка > &ПоследняяСсылка) — это "золотой стандарт" работы с большими выборками. Это гораздо эффективнее, чем выгрузка всей базы в ТаблицуЗначений на сервере 1С с последующим делением.
При работе с экстремально большими таблицами (миллионы строк) даже простой обход может вызвать переполнение памяти. В таких случаях мы рекомендуем рассмотреть стратегию "обработки с удалением":
Удалить().ТЗ.Количество() > 0.Внимание: Удаление строк из таблицы — операция тяжелая, поэтому этот метод стоит использовать только тогда, когда критически важно освобождать память в процессе работы алгоритма.
Подводя итог, можно сказать, что выбор метода зависит от контекста. Для простых вычислений идеально подходит Метод 1 (Индексы). Если нужно создавать подтаблицы — используйте Метод 3 (Скопировать с массивом строк). А при работе с базой данных всегда отдавайте предпочтение порционной выборке на уровне запроса.