Как ускорить массовое создание новой номенклатуры в 1С

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

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

Оптимизация обхода таблицы значений

Проанализируем исходный код, представленный в обсуждении. Одной из первых проблем является использование счетчика индекса в цикле Для. Ошибка «индекс за пределами массива» возникает из-за неправильного определения верхней границы цикла. В 1С индексы начинаются с 0, поэтому цикл должен идти до Количество() - 1.

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


Для каждого СтрТЗ Из ТабЗнч Цикл
    // Работаем напрямую со строкой таблицы
    ПолученныйКод = СтрЗаменить(СтрТЗ.Колонка1, Символы.НПП, "");
    // ... дальнейшая логика
КонецЦикла;

Вынос поиска статических данных из цикла

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

Правильным решением будет найти ссылку на нужный объект один раз перед началом цикла и передать её внутрь. Проанализируем пример оптимизации:


// Находим ссылку заранее
ВидНомТовар = Справочники.ВидыНоменклатуры.НайтиПоНаименованию("Товар");

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

Использование транзакций для ускорения записи

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

Группировка записей в одну общую транзакцию позволяет СУБД обработать данные в памяти и зафиксировать их одним блоком. Однако важно помнить, что слишком большая транзакция (например, на 100 000 элементов) может заблокировать таблицы для других пользователей. Оптимально использовать порции по 500–1000 элементов.


НачатьТранзакцию();
Счетчик = 0;

Для каждого СтрТЗ Из ТабЗнч Цикл
    // Создание и заполнение объекта
    // ...
    НоваяНоменклатура.Записать();
    
    Счетчик = Счетчик + 1;
    
    // Фиксируем порцию данных каждые 500 итераций
    Если Счетчик % 500 = 0 Тогда
        ЗафиксироватьТранзакцию();
        НачатьТранзакцию();
    КонецЕсли;
КонецЦикла;

Если ТранзакцияАктивна() Тогда
    ЗафиксироватьТранзакцию();
КонецЕсли;

Предварительная проверка существования объектов через запрос

Методы НайтиПоКоду или НайтиПоНаименованию внутри цикла — это «убийцы» производительности. Вместо этого рассмотрим стратегию «запрос + соответствие»:

  1. Выгрузим все коды из нашей таблицы значений в массив.
  2. Одним запросом найдем в базе данных все элементы Номенклатура, коды которых есть в этом массиве.
  3. Результат запроса выгрузим в объект Соответствие, где ключом будет код, а значением — ссылка.
  4. В основном цикле будем проверять наличие кода в Соответствии. Поиск в памяти (в объекте Соответствие) происходит мгновенно.

// 1. Формируем массив кодов
МассивКодов = ТабЗнч.ВыгрузитьКолонку("Колонка1");

// 2. Запрос к БД
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Код, Ссылка ИЗ Справочник.Номенклатура ГДЕ Код В (&МассивКодов)";
Запрос.УстановитьПараметр("МассивКодов", МассивКодов);

// 3. Кэшируем в Соответствие
СуществующаяНоменклатура = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    СуществующаяНоменклатура.Вставить(Выборка.Код, Выборка.Ссылка);
КонецЦикла;

// 4. Основной цикл
Для каждого СтрТЗ Из ТабЗнч Цикл
    ПолученныйКод = СтрТЗ.Колонка1;
    Если СуществующаяНоменклатура[ПолученныйКод] = Неопределено Тогда
        // Создаем новый элемент, так как его нет в кэше
    КонецЕсли;
КонецЦикла;

Использование режима загрузки данных

При массовом создании объектов часто не требуется выполнение бизнес-логики, заложенной в модулях объекта (проверки, заполнение связанных регистров, подписки на события). Чтобы отключить эти механизмы и ускорить запись, используем свойство ОбменДанными.Загрузка.

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


НоваяНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
// Заполнение реквизитов...
НоваяНоменклатура.ОбменДанными.Загрузка = Истина; 
НоваяНоменклатура.Записать();

Дополнительные рекомендации

Проанализируем еще несколько приемов, которые помогут сократить время выполнения задачи:

  1. ЗаполнитьЗначенияСвойств: Если имена колонок в вашей таблице значений совпадают с именами реквизитов справочника, используйте ЗаполнитьЗначенияСвойств(НоваяНоменклатура, СтрТЗ). Это работает быстрее, чем ручное присваивание каждой строки.
  2. Замер производительности: Всегда используйте встроенный инструмент «Замер производительности» в конфигураторе. Он покажет, какая именно строка кода занимает больше всего времени. Часто причина кроется не в самой записи, а в поиске единиц измерения или других справочников.
  3. Фоновое выполнение: При создании большого количества номенклатуры (десятки тысяч позиций) рекомендуется выносить процедуру в фоновое задание. Это позволит пользователю продолжать работу, пока сервер обрабатывает данные.

Применяя комплексный подход — использование транзакций, предварительное кэширование данных через запросы и флаг ОбменДанными.Загрузка — вы сможете сократить время создания номенклатуры с нескольких минут до нескольких секунд.

← На главную