При работе с крупными базами данных 1С программисты часто сталкиваются с задачей загрузки прайс-листов, объем которых превышает сотни тысяч строк. Например, ситуация, когда файл содержит 500 000 строк с ценами, а справочник Номенклатура насчитывает более 200 000 позиций. Стандартные методы загрузки в таких условиях либо работают крайне медленно (часы), либо приводят к аварийному завершению работы системы из-за нехватки памяти или ограничений интерфейса. В этой статье мы разберем основные стратегии оптимизации этого процесса.
Прежде всего, проанализируем ситуацию с типовым документом Установка цен номенклатуры. В большинстве конфигураций 1С (УТ 11, ERP, КА) форма этого документа перегружена логикой: при добавлении строк происходят пересчеты, проверки, форматирование и отображение представлений. Если в табличную часть попытаться загрузить 100 000 строк и более, клиентское приложение может «зависнуть» на десятки минут. Более того, существует технологическое ограничение на количество строк в табличной части (обычно до 100 000 для комфортной работы), после которого работа с документом в интерфейсе становится невозможной.
Для решения этой проблемы рассмотрим несколько эффективных подходов.
Это классический и наиболее рекомендуемый способ. Вместо того чтобы искать каждую номенклатуру в цикле методом НайтиПоРеквизиту, мы должны перенести всю тяжесть поиска на SQL-сервер. Разберем по шагам, как это реализовать:
ЧтениеТекста для CSV-файлов вместо COM-объекта Excel).Номенклатура по полю Артикул.Рассмотрим пример кода для реализации этой логики:
// Создаем таблицу значений с данными из файла
ТЗДанныеФайла = Новый ТаблицаЗначений;
ТЗДанныеФайла.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(50)));
ТЗДанныеФайла.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
// ... (здесь идет код заполнения ТЗ из файла) ...
МенеджерВТ = Новый МенеджерВременных Таблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// Помещаем ТЗ в запрос
Запрос.Текст =
"ВЫБРАТЬ
| Данные.Артикул КАК Артикул,
| Данные.Цена КАК Цена
|ПОМЕСТИТЬ ВТ_ДанныеФайла
|ИЗ
| &ТЗДанныеФайла КАК Данные;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| ВТ_ДанныеФайла.Цена КАК Цена
|ИЗ
| ВТ_ДанныеФайла КАК ВТ_ДанныеФайла
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ВТ_ДанныеФайла.Артикул = Номенклатура.Артикул
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа";
Запрос.УстановитьПараметр("ТЗДанныеФайла", ТЗДанныеФайла);
РезультатЗапроса = Запрос.Выполнить();
Важный нюанс: Если в файле могут быть дубли артикулов или в справочнике одному артикулу соответствует несколько позиций, запрос вернет избыточные данные. Проанализируем ситуацию: для корректной работы следует использовать ВЫБРАТЬ РАЗЛИЧНЫЕ или предварительно сворачивать ТЗ.
Если справочник Номенклатура не меняется в процессе загрузки, мы можем один раз прочитать все необходимые данные в оперативную память. Поиск по объекту Соответствие или Структура в 1С происходит мгновенно (используется хеш-таблица).
Разберем алгоритм:
Артикул -> Ссылка.Соответствие, где ключом будет артикул.
// Формируем карту поиска
КартаНоменклатуры = Новый Соответствие;
Запрос = Новый Запрос("ВЫБРАТЬ Артикул, Ссылка ИЗ Справочник.Номенклатура ГДЕ НЕ ЭтоГруппа И НЕ ПометкаУдаления");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Если артикулы дублируются, логика может быть сложнее (например, хранить массив ссылок)
КартаНоменклатуры.Вставить(СокращенныйЛП(Выборка.Артикул), Выборка.Ссылка);
КонецЦикла;
// Теперь при обходе файла поиск будет мгновенным
СсылкаНаТовар = КартаНоменклатуры.Получить(АртикулИзФайла);
Если СсылкаНаТовар <> Неопределено Тогда
// Заполняем данные
КонецЕсли;
Этот метод потребляет значительный объем оперативной памяти (для 200 000 ссылок потребуется несколько сотен мегабайт), поэтому его рекомендуется выполнять на 64-битном сервере приложений.
Когда мы нашли ссылки на номенклатуру, встает вопрос записи. Как мы выяснили, 500 000 строк в один документ Установка цен номенклатуры записывать нельзя. Рассмотрим способы обхода:
ОбменДанными.Загрузка = Истина. Это отключит лишние проверки в модуле объекта и ускорит запись в разы.ЦеныНоменклатуры (или аналогичный), минуя документ. Однако помните, что в этом случае вы теряете историю (кто и когда создал документ) и возможность типового пересчета цен.Выясним дополнительные факторы, которые влияют на скорость загрузки:
Индексация: Проверьте в конфигураторе, стоит ли у реквизита Артикул свойство "Индексировать". Без индекса поиск по 200-тысячному справочнику будет медленным вне зависимости от выбранного метода программирования.
Фоновые задания: Если сервер имеет много ядер, процесс обработки файла можно распараллелить. Разделите файл на 4-5 частей и запустите обработку каждой части в отдельном фоновом задании. Главное — следите за тем, чтобы разные потоки не пытались одновременно записывать данные в один и тот же регистр с пересекающимися измерениями, чтобы избежать блокировок SQL.
Чтение данных: Использование ЧтениеТекста для CSV/TXT файлов работает на порядок быстрее, чем Excel.Application. Если поставщик присылает XLSX, рассмотрите возможность использования внешних компонент для быстрого чтения Excel без установленного офиса.
Подводя итог, скажем: для быстрой загрузки 500 000 строк цен необходимо отказаться от интерактивного заполнения форм, использовать временные таблицы для пакетного поиска ссылок и дробить итоговые данные на несколько документов для обеспечения стабильности системы.