При разработке механизмов интеграции и загрузки данных из внешних источников (JSON, XML, Excel) программисты часто сталкиваются с классической проблемой производительности: необходимостью проверить наличие элемента в справочнике перед его созданием. Самый очевидный, но крайне неэффективный путь — выполнять Запрос внутри цикла обхода полученных данных. В этой статье мы подробно разберем, почему так делать не стоит, и проанализируем правильный алгоритм работы с использованием временных таблиц и механизмов платформы 1С:Предприятие 8.3.
Когда мы выполняем запрос к базе данных внутри цикла, система вынуждена на каждой итерации устанавливать соединение с СУБД, передавать параметры, компилировать план запроса и возвращать результат. Если в вашем JSON-файле 10 000 артикулов, это означает 10 000 отдельных обращений к серверу. Это создает колоссальную нагрузку на сетевой трафик и дисковую подсистему, превращая минутную загрузку в многочасовое ожидание. Наша задача — выполнить один запрос, который обработает весь массив данных разом.
Для начала нам необходимо считать данные из файла и привести их в структуру, понятную объекту Запрос. Лучшим вариантом будет использование объекта ТаблицаЗначений. Проанализируем пример подготовки такой таблицы:
// Создаем таблицу для хранения данных из файла
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
ТаблицаДанных.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));
// Читаем JSON (предположим, массив структур уже получен в МассивИзJSON)
Для Каждого ЭлементДанных Из МассивИзJSON Цикл
НоваяСтрока = ТаблицаДанных.Добавить();
// Обязательно используем СокрЛП для строковых ключей поиска
НоваяСтрока.Артикул = СокрЛП(ЭлементДанных.Артикул);
НоваяСтрока.Наименование = ЭлементДанных.Наименование;
КонецЦикла;
// Чтобы запрос работал быстрее и не выдавал дублей, сворачиваем таблицу
ТаблицаДанных.Свернуть("Артикул", "Наименование");
Важный нюанс: Перед загрузкой в запрос всегда очищайте строковые поля поиска от лишних пробелов с помощью функции СокрЛП. Для базы данных артикулы " 123" и "123" — это разные значения.
Теперь рассмотрим основное решение проблемы. Мы передадим нашу ТаблицуЗначений в запрос, поместим её во временную таблицу и выполним ЛЕВОЕ СОЕДИНЕНИЕ со справочником. Это позволит нам одним махом увидеть: какие элементы уже есть в базе, а какие нужно создать.
Разберем структуру запроса подробно:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_Данные.Артикул КАК Артикул,
| ВТ_Данные.Наименование КАК Наименование
|ПОМЕСТИТЬ ВТ_ВнешниеДанные
|ИЗ
| &ТЗ КАК ВТ_Данные
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_ВнешниеДанные.Артикул КАК Артикул,
| ВТ_ВнешниеДанные.Наименование КАК Наименование,
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| ВТ_ВнешниеДанные КАК ВТ_ВнешниеДанные
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ВТ_ВнешниеДанные.Артикул = Номенклатура.Артикул
|ГДЕ
| Номенклатура.Ссылка ЕСТЬ NULL";
Запрос.УстановитьПараметр("ТЗ", ТаблицаДанных);
РезультатЗапроса = Запрос.Выполнить();
Проанализируем, что здесь происходит: ЛЕВОЕ СОЕДИНЕНИЕ сопоставляет каждую строку из нашей таблицы с таблицей справочника по артикулу. Если в справочнике нет записи с таким артикулом, поле Номенклатура.Ссылка примет значение NULL. Условие ГДЕ Номенклатура.Ссылка ЕСТЬ NULL отсекает всё лишнее, оставляя только те данные, которых еще нет в системе.
После выполнения запроса мы получаем список только новых элементов. Теперь мы можем безопасно обходить выборку и создавать новые записи. Однако здесь кроется еще одна возможность для оптимизации — транзакции.
Запись каждого объекта по отдельности — это медленно. Рассмотрим, как правильно организовать запись пакетами:
Выборка = РезультатЗапроса.Выбрать();
Счетчик = 0;
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
НовыйЭлемент.Артикул = Выборка.Артикул;
НовыйЭлемент.Наименование = Выборка.Наименование;
// Включаем режим загрузки для ускорения записи и обхода лишних проверок
НовыйЭлемент.ОбменДанными.Загрузка = Истина;
Попытка
НовыйЭлемент.Записать();
Счетчик = Счетчик + 1;
Исключение
ЗафиксироватьТранзакцию(); // Нужно прервать текущую, если возникла ошибка
Сообщить("Ошибка при записи: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Фиксируем транзакцию каждые 500 элементов и начинаем новую
Если Счетчик % 500 = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
Для того чтобы сделать ваш код максимально надежным и быстрым, выясним еще несколько важных моментов:
ИНДЕКСИРОВАТЬ ПО в первом пакете запроса. Это ускорит операцию соединения.ПрочитатьJSON() в массив может занять всю оперативную память. Используйте объект ЧтениеJSON для последовательного перебора элементов без загрузки всего файла в память.ПередЗаписью), что существенно ускоряет процесс.ПО.Мы проанализировали ситуацию с медленной загрузкой данных и выяснили, что ключ к успеху в 1С — это массовая обработка данных. Используя связку ТаблицаЗначений -> Временная таблица -> Левое соединение, мы превращаем тысячи мелких операций в одну мощную серверную операцию. Такой подход не только экономит время, но и делает код профессиональным и масштабируемым.