В практике разработчика 1С часто возникает задача объединения данных из двух разных источников. Когда речь идет о нескольких десятках или сотнях строк, стандартный обход в цикле не вызывает проблем. Однако, если объем данных достигает 500 000 строк и более, обычные методы начинают работать критически медленно. Разберем, почему стандартный подход с использованием ЗаполнитьЗначенияСвойств неэффективен, и проанализируем альтернативные способы ускорения этой операции.
Рассмотрим классический пример, который чаще всего используют разработчики. Мы создаем цикл по добавляемой таблице и внутри него добавляем строку в основную таблицу, копируя данные. Выглядит это примерно так:
Для каждого СтрокаИзДобавляемой ИЗ ТЗ_Добавляемая Цикл
НоваяСтрока = ТЗ_Исходная.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИзДобавляемой);
КонецЦикла;
Давайте выясним причину медленной работы этого кода. Основная проблема заключается в функции ЗаполнитьЗначенияСвойств. При каждой итерации (а в нашем случае их 500 000) система выполняет операцию рефлексии: она анализирует структуру имен колонок обеих таблиц, ищет совпадения и только потом копирует данные. На больших объемах эти микрозадержки суммируются в минуты ожидания.
Если мы не можем уйти от использования цикла, проанализируем способы его оптимизации:
НоваяСтрока.ИмяКолонки = СтрокаИзДобавляемой.ИмяКолонки работает быстрее, так как исключает поиск соответствий.НоваяСтрока[ИндексКолонки] = СтрокаИзДобавляемой[ИндексКолонки] будет максимально быстрым для встроенного языка, так как исключается даже поиск по хеш-таблице имен колонок.
// Оптимизированный цикл с обращением по индексу
КолвоКолонок = ТЗ_Исходная.Колонки.Количество() - 1;
Для каждого СтрДобав ИЗ ТЗ_Добавляемая Цикл
НовСтр = ТЗ_Исходная.Добавить();
Для Инд = 0 По КолвоКолонок Цикл
НовСтр[Инд] = СтрДобав[Инд];
КонецЦикла;
КонецЦикла;
Одним из самых эффективных методов является перенос задачи объединения на сторону СУБД (или менеджера временных таблиц в случае файловой базы). Рассмотрим, как это реализовать. Мы передаем обе таблицы в запрос как параметры, помещаем их во временные таблицы и объединяем результатом выполнения запроса.
Рассмотрим пример кода:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
// Важно: колонки в ТЗ должны быть типизированы!
Запрос.Текст =
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Таб1 ИЗ &Таблица1 КАК Таблица1;
|ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Таб2 ИЗ &Таблица2 КАК Таблица2;
|ВЫБРАТЬ * ИЗ ВТ_Таб1
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ * ИЗ ВТ_Таб2";
Запрос.УстановитьПараметр("Таблица1", Таблица1);
Запрос.УстановитьПараметр("Таблица2", Таблица2);
ОбъединеннаяТаблица = Запрос.Выполнить().Выгрузить();
Проанализируем ситуацию: почему этот метод может быть в 2 и более раз быстрее? Во-первых, объединение происходит на низком уровне работы с данными в памяти или на сервере СУБД. Во-вторых, мы избегаем создания 500 000 объектов типа СтрокаТаблицыЗначений во встроенном языке, что значительно экономит память и время на работу сборщика мусора.
Важное ограничение: Чтобы этот метод сработал, колонки исходных таблиц должны иметь четко определенные типы. Если колонки имеют тип Произвольный, 1С не сможет создать временную таблицу в запросе, и вы получите ошибку.
Многие разработчики забывают про еще один критически важный фактор — индексы. Если в вашей целевой таблице (ТЗ_Исходная) созданы индексы через метод ТЗ.Индексы.Добавить(), то при каждом добавлении новой строки система будет обязана пересчитать этот индекс. На 500 000 строк это превратит операцию объединения в бесконечный процесс.
Выясним правильный алгоритм действий при наличии индексов:
Добавить для нужных колонок.Такой подход сокращает время работы в десятки раз, так как построение индекса по готовой таблице выполняется гораздо эффективнее, чем его инкрементальное обновление.
Иногда задача стоит не в полном объединении строк, а в добавлении данных в конкретную колонку уже существующей таблицы. В этом случае рассмотрим метод ВыгрузитьКолонку() и ЗагрузитьКолонку(). Работа с массивами в 1С происходит быстрее, чем построчный обход таблицы значений.
// Пример быстрого переноса данных одной колонки
МассивДанных = ТЗ_Добавляемая.ВыгрузитьКолонку("Сумма");
ТЗ_Исходная.ЗагрузитьКолонку(МассивДанных, "Сумма");
Хотя этот метод не позволяет «приклеивать» строки снизу (он замещает данные в существующих строках), его часто используют в связке с предварительным добавлением пустых строк, чтобы быстро заполнить их данными из другой таблицы с той же последовательностью записей.
Подведем итог нашему анализу и выберем оптимальную стратегию:
ЗаполнитьЗначенияСвойств в циклах, превышающих 10 000 итераций.Соблюдение этих правил позволит вам сократить время обработки данных с нескольких минут до нескольких секунд, обеспечивая высокую отзывчивость системы даже при работе с тяжелыми аналитическими отчетами или обработками выгрузки.