Перейти к содержимому

Не выполнять однотипные запросы в цикле

Директивный

v8std#std487диагностикаCreateQueryInCycle

#запросы#производительность

Однотипные данные получайте одним запросом, а не серией запросов в цикле. Список значений для отбора передавайте параметром (В (&Список)), для сложных наборов — через временную таблицу. Это касается и неявных запросов: ПолучитьОбъект(), разыменование ссылки через точку и НайтиПоНаименованию() внутри цикла — те же запросы к СУБД.

Запрос в цикле превращает одну операцию с СУБД в N операций: N раз компиляция плана, N сетевых обращений, N блокировок. На двух тестовых элементах это незаметно, на боевой тысяче — секунды и минуты. Это самая частая причина «медленной обработки» на проде при зелёных тестах.

Тот же приём в языке запросов:

ВЫБРАТЬ
БанковскиеСчета.Ссылка КАК Счет
ИЗ
Справочник.БанковскиеСчета КАК БанковскиеСчета
ГДЕ
БанковскиеСчета.Банк В (&БанкиДляОбработки)

Составной ключ — через временную таблицу

Заголовок раздела «Составной ключ — через временную таблицу»

Когда отбор идёт не по одному полю, а по комбинации (номенклатура + склад, номер + дата), В (&Список) не подходит. Таблица значений с ключами передаётся параметром и соединяется с данными:

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

Запрос в цикле не всегда выглядит как Новый Запрос. Каждая из этих конструкций внутри цикла — то же обращение к СУБД на каждой итерации:

  • Ссылка.Реквизит (разыменование через точку) — запрос за объектом;
  • ПолучитьОбъект(), НайтиПоНаименованию(), НайтиПоКоду();
  • обращение к виртуальной таблице (остатки, срез последних) с отбором по текущему элементу цикла.

Явные случаи подсвечивает диагностика CreateQueryInCycle; неявные она не видит — их выдаёт замер производительности: один и тот же текст запроса с счётчиком в тысячи выполнений.

В загрузке продаж из ДодоИс сопоставление номенклатуры и проверка ставки НДС выполняются для каждой строки выгрузки. Поиск «в лоб» — НайтиПоНаименованию и разыменование ставки в цикле — это два скрытых запроса на строку, тысячи запросов на дневную выгрузку. Правильно: до цикла один запрос собирает соответствие «внешний ключ → номенклатура, ставка НДС» в Соответствие, и цикл работает уже с ним — в коде стадии валидации это строка СтавкиНДСНоменклатуры[СтрокаДодоИс.Номенклатура].

Пакетный запрос к справочнику банковских счетов в конфигураторе 1С
Рис. 1. Один запрос вместо серии запросов в цикле
  • Следующий запрос действительно зависит от результата предыдущего и не сводится к пакетному запросу или временной таблице (редкий случай — обычно сводится).
  • Порционная обработка больших объёмов (чанкование по 1000 записей): серия запросов осознанна, но каждый запрос обрабатывает пачку, а не один элемент.
  • Разовые административные скрипты на малом объёме, где читаемость важнее миллисекунд, — допустимо, но не в коде, попадающем в конфигурацию.