Как избежать запросов в цикле при проверке и создании элементов справочника из JSON файла?

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

При разработке механизмов интеграции и загрузки данных из внешних источников (JSON, XML, Excel) программисты часто сталкиваются с классической проблемой производительности: необходимостью проверить наличие элемента в справочнике перед его созданием. Самый очевидный, но крайне неэффективный путь — выполнять Запрос внутри цикла обхода полученных данных. В этой статье мы подробно разберем, почему так делать не стоит, и проанализируем правильный алгоритм работы с использованием временных таблиц и механизмов платформы 1С:Предприятие 8.3.

Почему запросы в цикле — это антипаттерн?

Когда мы выполняем запрос к базе данных внутри цикла, система вынуждена на каждой итерации устанавливать соединение с СУБД, передавать параметры, компилировать план запроса и возвращать результат. Если в вашем JSON-файле 10 000 артикулов, это означает 10 000 отдельных обращений к серверу. Это создает колоссальную нагрузку на сетевой трафик и дисковую подсистему, превращая минутную загрузку в многочасовое ожидание. Наша задача — выполнить один запрос, который обработает весь массив данных разом.

Шаг 1. Подготовка данных: от JSON к Таблице Значений

Для начала нам необходимо считать данные из файла и привести их в структуру, понятную объекту Запрос. Лучшим вариантом будет использование объекта ТаблицаЗначений. Проанализируем пример подготовки такой таблицы:


// Создаем таблицу для хранения данных из файла
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
ТаблицаДанных.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));

// Читаем JSON (предположим, массив структур уже получен в МассивИзJSON)
Для Каждого ЭлементДанных Из МассивИзJSON Цикл
    НоваяСтрока = ТаблицаДанных.Добавить();
    // Обязательно используем СокрЛП для строковых ключей поиска
    НоваяСтрока.Артикул = СокрЛП(ЭлементДанных.Артикул);
    НоваяСтрока.Наименование = ЭлементДанных.Наименование;
КонецЦикла;

// Чтобы запрос работал быстрее и не выдавал дублей, сворачиваем таблицу
ТаблицаДанных.Свернуть("Артикул", "Наименование");

Важный нюанс: Перед загрузкой в запрос всегда очищайте строковые поля поиска от лишних пробелов с помощью функции СокрЛП. Для базы данных артикулы " 123" и "123" — это разные значения.

Шаг 2. Использование временных таблиц в запросе

Теперь рассмотрим основное решение проблемы. Мы передадим нашу ТаблицуЗначений в запрос, поместим её во временную таблицу и выполним ЛЕВОЕ СОЕДИНЕНИЕ со справочником. Это позволит нам одним махом увидеть: какие элементы уже есть в базе, а какие нужно создать.

Разберем структуру запроса подробно:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ВТ_Данные.Артикул КАК Артикул,
    |    ВТ_Данные.Наименование КАК Наименование
    |ПОМЕСТИТЬ ВТ_ВнешниеДанные
    |ИЗ
    |    &ТЗ КАК ВТ_Данные
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    ВТ_ВнешниеДанные.Артикул КАК Артикул,
    |    ВТ_ВнешниеДанные.Наименование КАК Наименование,
    |    Номенклатура.Ссылка КАК Ссылка
    |ИЗ
    |    ВТ_ВнешниеДанные КАК ВТ_ВнешниеДанные
    |        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
    |        ПО ВТ_ВнешниеДанные.Артикул = Номенклатура.Артикул
    |ГДЕ
    |    Номенклатура.Ссылка ЕСТЬ NULL";

Запрос.УстановитьПараметр("ТЗ", ТаблицаДанных);
РезультатЗапроса = Запрос.Выполнить();

Проанализируем, что здесь происходит: ЛЕВОЕ СОЕДИНЕНИЕ сопоставляет каждую строку из нашей таблицы с таблицей справочника по артикулу. Если в справочнике нет записи с таким артикулом, поле Номенклатура.Ссылка примет значение NULL. Условие ГДЕ Номенклатура.Ссылка ЕСТЬ NULL отсекает всё лишнее, оставляя только те данные, которых еще нет в системе.

Шаг 3. Обработка результата и создание объектов

После выполнения запроса мы получаем список только новых элементов. Теперь мы можем безопасно обходить выборку и создавать новые записи. Однако здесь кроется еще одна возможность для оптимизации — транзакции.

Запись каждого объекта по отдельности — это медленно. Рассмотрим, как правильно организовать запись пакетами:


Выборка = РезультатЗапроса.Выбрать();

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

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

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

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

Для того чтобы сделать ваш код максимально надежным и быстрым, выясним еще несколько важных моментов:

  1. Индексирование временной таблицы: Если во внешней таблице данных более пары тысяч строк, обязательно добавьте ИНДЕКСИРОВАТЬ ПО в первом пакете запроса. Это ускорит операцию соединения.
  2. Потоковое чтение JSON: Если JSON-файл весит сотни мегабайт, метод ПрочитатьJSON() в массив может занять всю оперативную память. Используйте объект ЧтениеJSON для последовательного перебора элементов без загрузки всего файла в память.
  3. ОбменДанными.Загрузка = Истина: Установка этого свойства у объекта перед записью отключает выполнение прикладных проверок в модулях объекта (например, проверки заполнения или сложные расчеты в ПередЗаписью), что существенно ускоряет процесс.
  4. Поиск по нескольким полям: Если артикул не является уникальным идентификатором, вы можете добавить в таблицу значений и в условие соединения запроса дополнительные поля (например, "Производитель" или "Код"). Запрос всё равно останется один, просто увеличится количество условий в ПО.

Итоги

Мы проанализировали ситуацию с медленной загрузкой данных и выяснили, что ключ к успеху в 1С — это массовая обработка данных. Используя связку ТаблицаЗначений -> Временная таблица -> Левое соединение, мы превращаем тысячи мелких операций в одну мощную серверную операцию. Такой подход не только экономит время, но и делает код профессиональным и масштабируемым.

← На главную