Как быстро объединить две таблицы значений в 1С при большом объеме данных?

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

В практике разработчика 1С часто возникает задача объединения данных из двух разных источников. Когда речь идет о нескольких десятках или сотнях строк, стандартный обход в цикле не вызывает проблем. Однако, если объем данных достигает 500 000 строк и более, обычные методы начинают работать критически медленно. Разберем, почему стандартный подход с использованием ЗаполнитьЗначенияСвойств неэффективен, и проанализируем альтернативные способы ускорения этой операции.

Проблема стандартного метода и использование цикла

Рассмотрим классический пример, который чаще всего используют разработчики. Мы создаем цикл по добавляемой таблице и внутри него добавляем строку в основную таблицу, копируя данные. Выглядит это примерно так:


Для каждого СтрокаИзДобавляемой ИЗ ТЗ_Добавляемая Цикл
    НоваяСтрока = ТЗ_Исходная.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИзДобавляемой);
КонецЦикла;

Давайте выясним причину медленной работы этого кода. Основная проблема заключается в функции ЗаполнитьЗначенияСвойств. При каждой итерации (а в нашем случае их 500 000) система выполняет операцию рефлексии: она анализирует структуру имен колонок обеих таблиц, ищет совпадения и только потом копирует данные. На больших объемах эти микрозадержки суммируются в минуты ожидания.

Если мы не можем уйти от использования цикла, проанализируем способы его оптимизации:

  1. Прямое обращение по именам: Использование конструкции НоваяСтрока.ИмяКолонки = СтрокаИзДобавляемой.ИмяКолонки работает быстрее, так как исключает поиск соответствий.
  2. Обращение по индексу: Если структуры таблиц идентичны, обращение НоваяСтрока[ИндексКолонки] = СтрокаИзДобавляемой[ИндексКолонки] будет максимально быстрым для встроенного языка, так как исключается даже поиск по хеш-таблице имен колонок.
  3. Микрооптимизация VM: Как отмечалось в обсуждении, запись тела цикла в одну строку может дать небольшой прирост (около 10-15%) на экстремальных объемах, так как виртуальная машина 1С тратит меньше ресурсов на переходы между строками исполняемого кода.

// Оптимизированный цикл с обращением по индексу
КолвоКолонок = ТЗ_Исходная.Колонки.Количество() - 1;
Для каждого СтрДобав ИЗ ТЗ_Добавляемая Цикл
    НовСтр = ТЗ_Исходная.Добавить();
    Для Инд = 0 По КолвоКолонок Цикл
        НовСтр[Инд] = СтрДобав[Инд];
    КонецЦикла;
КонецЦикла;

Использование механизма запросов (ОБЪЕДИНИТЬ ВСЕ)

Одним из самых эффективных методов является перенос задачи объединения на сторону СУБД (или менеджера временных таблиц в случае файловой базы). Рассмотрим, как это реализовать. Мы передаем обе таблицы в запрос как параметры, помещаем их во временные таблицы и объединяем результатом выполнения запроса.

Рассмотрим пример кода:


Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

// Важно: колонки в ТЗ должны быть типизированы!
Запрос.Текст = 
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Таб1 ИЗ &Таблица1 КАК Таблица1;
|ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Таб2 ИЗ &Таблица2 КАК Таблица2;
|ВЫБРАТЬ * ИЗ ВТ_Таб1
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ * ИЗ ВТ_Таб2";

Запрос.УстановитьПараметр("Таблица1", Таблица1);
Запрос.УстановитьПараметр("Таблица2", Таблица2);

ОбъединеннаяТаблица = Запрос.Выполнить().Выгрузить();

Проанализируем ситуацию: почему этот метод может быть в 2 и более раз быстрее? Во-первых, объединение происходит на низком уровне работы с данными в памяти или на сервере СУБД. Во-вторых, мы избегаем создания 500 000 объектов типа СтрокаТаблицыЗначений во встроенном языке, что значительно экономит память и время на работу сборщика мусора.

Важное ограничение: Чтобы этот метод сработал, колонки исходных таблиц должны иметь четко определенные типы. Если колонки имеют тип Произвольный, 1С не сможет создать временную таблицу в запросе, и вы получите ошибку.

Влияние индексов на скорость объединения

Многие разработчики забывают про еще один критически важный фактор — индексы. Если в вашей целевой таблице (ТЗ_Исходная) созданы индексы через метод ТЗ.Индексы.Добавить(), то при каждом добавлении новой строки система будет обязана пересчитать этот индекс. На 500 000 строк это превратит операцию объединения в бесконечный процесс.

Выясним правильный алгоритм действий при наличии индексов:

  1. Очищаем существующие индексы у целевой таблицы.
  2. Выполняем объединение (через запрос или быстрый цикл).
  3. Создаем индексы заново одним методом Добавить для нужных колонок.

Такой подход сокращает время работы в десятки раз, так как построение индекса по готовой таблице выполняется гораздо эффективнее, чем его инкрементальное обновление.

Метод «ЗагрузитьКолонку» для частичного объединения

Иногда задача стоит не в полном объединении строк, а в добавлении данных в конкретную колонку уже существующей таблицы. В этом случае рассмотрим метод ВыгрузитьКолонку() и ЗагрузитьКолонку(). Работа с массивами в 1С происходит быстрее, чем построчный обход таблицы значений.


// Пример быстрого переноса данных одной колонки
МассивДанных = ТЗ_Добавляемая.ВыгрузитьКолонку("Сумма");
ТЗ_Исходная.ЗагрузитьКолонку(МассивДанных, "Сумма");

Хотя этот метод не позволяет «приклеивать» строки снизу (он замещает данные в существующих строках), его часто используют в связке с предварительным добавлением пустых строк, чтобы быстро заполнить их данными из другой таблицы с той же последовательностью записей.

Выводы и рекомендации

Подведем итог нашему анализу и выберем оптимальную стратегию:

Соблюдение этих правил позволит вам сократить время обработки данных с нескольких минут до нескольких секунд, обеспечивая высокую отзывчивость системы даже при работе с тяжелыми аналитическими отчетами или обработками выгрузки.

← На главную