Как оптимизировать запросы в 1С и почему нельзя выполнять их в цикле?

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

В практике разработки на платформе 1С:Предприятие 8.3 одной из наиболее распространенных причин низкой производительности систем является выполнение запросов к базе данных внутри циклов. В этой статье мы подробно проанализируем, почему такой подход считается «антипаттерном», разберем типичные ошибки и рассмотрим правильные методики написания эффективного кода. Совместно мы научимся переводить «тяжелый» код на рельсы оптимальной работы через использование массивов и временных таблиц.

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

Давайте проанализируем ситуацию, когда нам нужно получить данные для списка объектов, например, остатки товаров для массива ссылок. Если мы поместим Запрос.Выполнить() внутрь цикла Для Каждого, программа будет обращаться к СУБД (SQL Server или PostgreSQL) на каждой итерации. Разберем по шагам, что происходит в этот момент:

  1. Происходит вызов серверного контекста.
  2. Платформа формирует SQL-запрос.
  3. СУБД тратит время на парсинг запроса и построение плана выполнения.
  4. Устанавливаются сетевые соединения и передаются данные.

Если в цикле 1000 элементов, эти действия повторятся 1000 раз. Суммарная задержка (overhead) на организацию связи с СУБД превысит время самого выполнения запроса. Выясним причину: основной ресурс тратится не на поиск данных, а на обслуживание самого факта обращения к базе.

Решение №1: Использование массивов и оператора «В» в запросе

Рассмотрим стандартный пример: у нас есть массив ссылок на товары МассивТоваров, и нам нужно получить их артикулы. Вместо того чтобы перебирать массив и выполнять запрос для каждого элемента, мы проанализируем, как сделать это одним махом.

Сначала подготовим данные и передадим их в параметры запроса. Рассмотрим правильный код:


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

// Создаем запрос один раз вне цикла
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.Артикул КАК Артикул
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	Номенклатура.Ссылка В (&СписокСсылок)";

Запрос.УстановитьПараметр("СписокСсылок", СписокСсылок);

// Выполняем запрос один раз
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Обрабатываем данные сразу для всех элементов
    Сообщить(Выборка.Артикул);
КонецЦикла;

В данном примере мы используем оператор В, который позволяет СУБД эффективно обработать список значений. Это сокращает количество обращений к базе данных до одного, что дает колоссальный прирост скорости при больших объемах данных.

Решение №2: Использование Временных Таблиц (ВТ)

В более сложных ситуациях, когда данных много или они представляют собой сложную структуру (например, таблицу значений с количеством и ценой), одного массива может быть недостаточно. Проанализируем ситуацию, когда нам нужно сопоставить данные из нашей таблицы с данными в базе. В этом случае нам поможет объект МенеджерВременныхТаблиц.

Разберем алгоритм по шагам:

  1. Создаем Таблицу Значений с нужными колонками.
  2. Помещаем эту таблицу во временную таблицу в запросе.
  3. Выполняем соединение (JOIN) временной таблицы с реальными таблицами базы данных.

Посмотрим на пример реализации:


// Допустим, у нас есть ТаблицаТоваров с колонками Номенклатура и Склад
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Помещаем ТЗ во временную таблицу
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЗ.Номенклатура КАК Номенклатура,
    |	ТЗ.Склад КАК Склад
    |ПОМЕСТИТЬ ВТ_ДанныеФайла
    |ИЗ
    |	&ТЗ КАК ТЗ";
    
Запрос.УстановитьПараметр("ТЗ", ТаблицаТоваров);
Запрос.Выполнить();

// Теперь выполняем основной запрос с соединением
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВТ.Номенклатура КАК Номенклатура,
    |	ЕСТЬNULL(ТоварыНаСкладахОстатки.В НаличииОстаток, 0) КАК Остаток
    |ИЗ
    |	ВТ_ДанныеФайла КАК ВТ
    |ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки( , 
    |	Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура ИЗ ВТ_ДанныеФайла)) КАК ТоварыНаСкладахОстатки
    |ПО ВТ.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура";

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

Важный момент: при использовании временных таблиц всегда старайтесь индексировать поля, по которым будет происходить соединение. Это значительно ускоряет работу СУБД. Для этого в первом запросе после ПОМЕСТИТЬ можно добавить инструкцию ИНДЕКСИРОВАТЬ ПО.

Решение №3: Обработка данных через Соответствие (Map)

Часто программисты задаются вопросом: «Как мне быстро найти данные из результата запроса для конкретной строки моей исходной таблицы?». Если мы будем использовать метод Найти() в таблице значений внутри цикла, мы снова получим падение производительности.

Разберем эффективный метод — использование объекта Соответствие. Рассмотрим, как это работает:

  1. Выполняем запрос один раз (как в Решении №1).
  2. Выгружаем результат запроса в Соответствие, где ключом будет Ссылка, а значением — структура с данными.
  3. В основном цикле получаем данные из соответствия по ключу. Это происходит мгновенно в памяти.

// Заполняем соответствие
КэшДанных = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    КэшДанных.Вставить(Выборка.Ссылка, Выборка.Артикул);
КонецЦикла;

// Основной цикл обработки
Для Каждого Строка Из МояТаблица Цикл
    Артикул = КэшДанных.Получить(Строка.Номенклатура);
    Если Артикул <> Неопределено Тогда
        Строка.Артикул = Артикул;
    КонецЕсли;
КонецЦикла;

Общие рекомендации по оптимизации

Подводя итог нашему анализу, сформулируем основные правила «гигиены» при написании кода в 1С:

Проанализировав вышеописанные методы, мы видим, что оптимизация в 1С требует структурного подхода. Переход от запросов в цикле к пакетным запросам и временным таблицам не только ускоряет программу, но и делает код более профессиональным и устойчивым к росту базы данных.

← На главную