Можно ли использовать запросы в цикле в 1С 8.3 и как это влияет на производительность?

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

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

Природа проблемы: почему запросы в цикле работают медленно?

Для понимания сути проблемы разберем технологическую цепочку выполнения любого запроса. Проблема заключается не только в скорости работы самой СУБД (MS SQL Server, PostgreSQL и др.), но и в накладных расходах на сетевое взаимодействие, которые называют Round-trip.

Рассмотрим по шагам, что происходит, когда выполняется Запрос.Выполнить():

  1. Сервер приложений 1С формирует пакет данных для СУБД.
  2. Пакет передается по сети.
  3. СУБД принимает запрос, выполняет его синтаксический анализ и компиляцию плана выполнения.
  4. СУБД считывает данные с диска или из кэша.
  5. Результат передается обратно по сети на сервер 1С.
  6. Сервер 1С десериализует данные и помещает их в объектную модель.

Даже если сам поиск в базе данных занимает 0,001 секунды, вся цепочка действий может занять 0,01 секунды. Если мы поместим такой запрос в цикл на 1000 итераций, то суммарная задержка только на «хождение» данных составит 10 секунд. В то же время один единый запрос, выбирающий те же 1000 записей за раз, отработает практически мгновенно, так как Round-trip произойдет единожды.

Проблема «N+1» и неявные запросы

Часто программисты допускают использование запросов в цикле неосознанно. Это явление в профессиональной среде называется проблемой N+1: вы делаете один запрос, чтобы получить список ссылок (например, товаров в документе), а затем выполняете N дополнительных обращений к базе данных для получения свойств каждого товара.

Разберем типичные ситуации, где возникают скрытые запросы:

1. Обращение через точку к реквизитам ссылочных типов.

Проанализируем пример кода:


Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Артикул = Выборка.Номенклатура.Артикул; // Скрытый запрос!
    Сообщить(Артикул);
КонецЦикла;

В данном случае, если поле Артикул не было получено в основном тексте запроса через соединение, платформа 1С будет вынуждена обращаться к СУБД на каждой итерации цикла, чтобы «дочитать» это значение. Это классический пример запроса в цикле, замаскированного под обычное обращение к свойству.

2. Получение представлений и обработка событий в модуле менеджера.

Выясним причину, почему динамические списки или отчеты могут тормозить. Если в модуле менеджера объекта (например, справочника «Контрагенты») определена процедура ОбработкаПолученияПредставления, то при каждом выводе строки в список или отчет платформа может инициировать отдельный вызов сервера для формирования текста представления. Если таких строк тысячи, система начинает «захлебываться» в мелких запросах.

Стандарты разработки и позиция фирмы 1С

Согласно стандартам ИТС, разработчикам рекомендуется минимизировать общее количество обращений к СУБД. Однако в пункте 1.2 рекомендаций есть важное уточнение: не следует пытаться любой ценой перенести абсолютно все вычисления в один сверхсложный запрос.

Проанализируем ситуацию, когда сложные запросы могут быть хуже простых в цикле:

Как правильно оптимизировать код?

Рассмотрим шаги по избавлению от запросов в цикле на практическом примере. Допустим, нам нужно получить остатки по списку товаров, который мы обходим в цикле.

Неправильный подход (запрос в цикле):


Для Каждого СтрокаТЧ Из Объект.Товары Цикл
    Запрос = Новый Запрос("ВЫБРАТЬ Остатки.КоличествоОстаток ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки( , Номенклатура = &Номенклатура) КАК Остатки");
    Запрос.УстановитьПараметр("Номенклатура", СтрокаТЧ.Номенклатура);
    // ... выполнение и обработка
КонецЦикла;

Правильный подход (единый запрос):

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

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

Пример оптимизированного кода:


МассивТоваров = Объект.Товары.ВыгрузитьКолонку("Номенклатура");
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Остатки.Номенклатура КАК Номенклатура,
    |   Остатки.КоличествоОстаток КАК Остаток
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&МассивТоваров)) КАК Остатки";

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

ТаблицаОстатков = Новый Соответствие;
Для Каждого Стр Из Результат Цикл
    ТаблицаОстатков.Вставить(Стр.Номенклатура, Стр.Остаток);
КонецЦикла;

Для Каждого СтрокаТЧ Из Объект.Товары Цикл
    ТекОстаток = ТаблицаОстатков[СтрокаТЧ.Номенклатура];
    // Обработка данных без обращений к БД
КонецЦикла;

Современные реалии платформы 8.3

Нужно признать, что в современных версиях платформы (8.3.18 и выше) реализовано агрессивное кэширование. Если вы запрашиваете данные одного и того же объекта в цикле, платформа может вернуть их из кэша сервера приложений, не обращаясь к SQL-серверу. Это создает иллюзию того, что «запросы в цикле работают быстро».

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

Подводя итог, проанализируем позицию «уважаемого программиста» из обсуждения. Скорее всего, он имел в виду, что в простых задачах на мощном оборудовании разница в 0,1 секунды незаметна для пользователя. Однако для профессионального разработчика 1С правило «запрос в цикле — это табу» остается фундаментальным принципом написания масштабируемого и надежного кода.

← На главную