При загрузке больших объемов данных в 1С программисты часто сталкиваются с проблемой низкой производительности. Создание нескольких тысяч элементов справочника Номенклатура может занимать значительное время, если код написан без учета особенностей работы платформы и системы управления базами данных (СУБД). Разберем по шагам, как оптимизировать этот процесс, начиная от простых исправлений в циклах и заканчивая продвинутыми архитектурными приемами.
Проанализируем исходный код, представленный в обсуждении. Одной из первых проблем является использование счетчика индекса в цикле Для. Ошибка «индекс за пределами массива» возникает из-за неправильного определения верхней границы цикла. В 1С индексы начинаются с 0, поэтому цикл должен идти до Количество() - 1.
Однако, чтобы не тратить время на работу с индексами и избежать подобных ошибок, рекомендуется использовать конструкцию Для каждого. Это не только делает код чище, но и работает немного быстрее на уровне платформы. Посмотрим на пример правильного обхода таблицы:
Для каждого СтрТЗ Из ТабЗнч Цикл
// Работаем напрямую со строкой таблицы
ПолученныйКод = СтрЗаменить(СтрТЗ.Колонка1, Символы.НПП, "");
// ... дальнейшая логика
КонецЦикла;
Рассмотрим ситуацию, когда для каждого нового элемента справочника задается один и тот же ВидНоменклатуры (например, "Товар"). Вызов метода НайтиПоНаименованию внутри цикла заставляет систему выполнять запрос к базе данных на каждой итерации. Если в таблице 7500 строк, программа сделает 7500 лишних запросов.
Правильным решением будет найти ссылку на нужный объект один раз перед началом цикла и передать её внутрь. Проанализируем пример оптимизации:
// Находим ссылку заранее
ВидНомТовар = Справочники.ВидыНоменклатуры.НайтиПоНаименованию("Товар");
Для каждого СтрТЗ Из ТабЗнч Цикл
НоваяНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
// Используем уже найденную ссылку
НоваяНоменклатура.ВидНоменклатуры = ВидНомТовар;
// ...
НоваяНоменклатура.Записать();
КонецЦикла;
Одной из самых ресурсоемких операций в 1С является метод Записать(). По умолчанию каждая запись объекта выполняется в отдельной транзакции. Это означает, что для каждого элемента СУБД выполняет полный цикл фиксации данных на диске, что крайне медленно. Выясним, как это ускорить.
Группировка записей в одну общую транзакцию позволяет СУБД обработать данные в памяти и зафиксировать их одним блоком. Однако важно помнить, что слишком большая транзакция (например, на 100 000 элементов) может заблокировать таблицы для других пользователей. Оптимально использовать порции по 500–1000 элементов.
НачатьТранзакцию();
Счетчик = 0;
Для каждого СтрТЗ Из ТабЗнч Цикл
// Создание и заполнение объекта
// ...
НоваяНоменклатура.Записать();
Счетчик = Счетчик + 1;
// Фиксируем порцию данных каждые 500 итераций
Если Счетчик % 500 = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
Методы НайтиПоКоду или НайтиПоНаименованию внутри цикла — это «убийцы» производительности. Вместо этого рассмотрим стратегию «запрос + соответствие»:
Номенклатура, коды которых есть в этом массиве.Соответствие, где ключом будет код, а значением — ссылка.Соответствии. Поиск в памяти (в объекте Соответствие) происходит мгновенно.
// 1. Формируем массив кодов
МассивКодов = ТабЗнч.ВыгрузитьКолонку("Колонка1");
// 2. Запрос к БД
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Код, Ссылка ИЗ Справочник.Номенклатура ГДЕ Код В (&МассивКодов)";
Запрос.УстановитьПараметр("МассивКодов", МассивКодов);
// 3. Кэшируем в Соответствие
СуществующаяНоменклатура = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СуществующаяНоменклатура.Вставить(Выборка.Код, Выборка.Ссылка);
КонецЦикла;
// 4. Основной цикл
Для каждого СтрТЗ Из ТабЗнч Цикл
ПолученныйКод = СтрТЗ.Колонка1;
Если СуществующаяНоменклатура[ПолученныйКод] = Неопределено Тогда
// Создаем новый элемент, так как его нет в кэше
КонецЕсли;
КонецЦикла;
При массовом создании объектов часто не требуется выполнение бизнес-логики, заложенной в модулях объекта (проверки, заполнение связанных регистров, подписки на события). Чтобы отключить эти механизмы и ускорить запись, используем свойство ОбменДанными.Загрузка.
Установка этого флага в значение Истина сообщает платформе, что объект загружается из внешнего источника и «тяжелые» процедуры ПередЗаписью или ПриЗаписи выполнять не нужно (если в них стоит соответствующее условие). Это может ускорить процесс в несколько раз.
НоваяНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
// Заполнение реквизитов...
НоваяНоменклатура.ОбменДанными.Загрузка = Истина;
НоваяНоменклатура.Записать();
Проанализируем еще несколько приемов, которые помогут сократить время выполнения задачи:
ЗаполнитьЗначенияСвойств(НоваяНоменклатура, СтрТЗ). Это работает быстрее, чем ручное присваивание каждой строки.Применяя комплексный подход — использование транзакций, предварительное кэширование данных через запросы и флаг ОбменДанными.Загрузка — вы сможете сократить время создания номенклатуры с нескольких минут до нескольких секунд.