При импорте данных из внешних источников (например, из файлов Excel) программисты 1С часто сталкиваются с проблемой низкой скорости обработки информации. В этом материале разберем типичную ситуацию: необходимо обойти строки из таблицы Excel, найти в базе 1С 7.7 все элементы справочника Номенклатура с соответствующим артикулом и записать для них новый штрихкод.
Рассмотрим частую ошибку, которая приводит к критическому замедлению или бесконечному циклу при попытке записать данные. В исходном коде разработчики часто используют полный перебор справочника на каждую строку файла Excel:
// Неоптимальный код с ошибкой обхода
Спр = СоздатьОбъект("Справочник.Номенклатура");
Для х = 2 По КоличествоСтрок Цикл
Артикул = СокрЛП(Строка(Excel.Cells(х, 1).Value));
Штрихкод = СокрЛП(Строка(Excel.Cells(х, 2).Value));
Спр.ВыбратьЭлементы();
Пока Спр.Выбран() = 1 Цикл // ОШИБКА: бесконечный цикл!
Если СокрЛП(Спр.Артикул) = Артикул Тогда
Спр.Штрихкод = Штрихкод;
Спр.Записать();
КонецЕсли;
КонецЦикла;
КонецЦикла;
В приведенном фрагменте присутствуют сразу два узких места:
Спр.Выбран() проверяет, выбран ли элемент в данный момент, но сам по себе не осуществляет переход к следующему элементу. Для позиционирования на следующей записи справочника необходимо использовать метод Спр.ПолучитьЭлемент().Самый быстрый и штатный способ найти элементы по конкретному значению реквизита в 1С 7.7 — применение метода ВыбратьЭлементыПоРеквизиту(). Он позволяет платформе выполнять выборку с использованием индексов базы данных, не просматривая лишние записи.
Важное требование платформы: Данный метод работает только для тех реквизитов, у которых в Конфигураторе включено свойство «Сортировка» или «Отбор по реквизиту». В противном случае 1С выдаст ошибку выполнения.
Посмотрим на итоговый правильный и быстродействующий код:
Спр = СоздатьОбъект("Справочник.Номенклатура");
Для х = 2 По КоличествоСтрок Цикл
Сообщить("Обрабатываем строку: " + х);
Артикул = СокрЛП(Строка(Excel.Cells(х, 1).Value));
Штрихкод = СокрЛП(Строка(Excel.Cells(х, 2).Value));
// Выбираем только те элементы, у которых Артикул совпадает с искомым
Спр.ВыбратьЭлементыПоРеквизиту("Артикул", Артикул, 0, 0);
Пока Спр.ПолучитьЭлемент() = 1 Цикл
Спр.Штрихкод = Штрихкод;
Спр.Записать();
КонецЦикла;
КонецЦикла;
Данный подход сокращает время выполнения с десятков минут до нескольких секунд.
Если по каким-то причинам включить сортировку/отбор для реквизита в Конфигураторе невозможно, выберем альтернативный вариант: загнать справочник в оперативную память через ТаблицаЗначений.
Проанализируем алгоритм:
Артикул.НайтиСтроки() или позиционирования методом НайтиЗначение().
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("Артикул");
ТЗ.НоваяКолонка("Элемент");
Спр = СоздатьОбъект("Справочник.Номенклатура");
Спр.ВыбратьЭлементы();
Пока Спр.ПолучитьЭлемент() = 1 Цикл
ТЗ.НоваяСтрока();
ТЗ.Артикул = СокрЛП(Спр.Артикул);
ТЗ.Элемент = Спр.ТекущийЭлемент();
КонецЦикла;
// Сортировка ускоряет поиск в памяти
ТЗ.Сортировать("Артикул");
// Далее при совпадении позиционируемся на объект и меняем реквизит
СпрОбъект = СоздатьОбъект("Справочник.Номенклатура");
// Поиск и обновление в цикле по Excel...
Попытка решить задачу обновления данных через встроенный «черный запрос» 1С 7.7 имеет важную специфику: запросы в 1С 7.7 работают только на чтение. Выполнить команду обновления данных непосредственно внутри запроса невозможно.
Если использовать запрос для поиска элементов, код будет выглядеть следующим образом:
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(ПоискНоменклатуры)
|Номенклатура = Справочник.Номенклатура.ТекущийЭлемент;
|Артикул = Справочник.Номенклатура.Артикул;
|Группировка Номенклатура без групп;
|Условие(СокрЛП(Артикул) = ИскомыйАртикул);
|"//}}ЗАПРОС;
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
Пока Запрос.Группировка(1) = 1 Цикл
ЭлементНоменклатуры = Запрос.Номенклатура;
// Для изменения необходимо позиционировать объект справочника
Спр.НайтиЭлемент(ЭлементНоменклатуры);
Спр.Штрихкод = Штрихкод;
Спр.Записать();
КонецЦикла;
КонецЕсли;
Запуск такого запроса на каждой итерации цикла Excel по производительности уступает методу ВыбратьЭлементыПоРеквизиту(), поэтому использование `ВыбратьЭлементыПоРеквизиту` остается наиболее предпочтительным.
Чтобы заставить обработку работать с максимальной скоростью, учтем следующие моменты:
НачатьТранзакцию() и ЗафиксироватьТранзакцию() ускоряет физическое сохранение файлов на диск в разы.Excel.Cells(x, y).Value работает медленно из-за накладных расходов COM-соединения. Для ускорения рекомендуется предварительно выгрузить весь лист Excel в двухмерный массив через Excel.Range(...).Value или сохранить файл в формат CSV и прочитать его встроенным объектом Текст.