Как оптимизировать запрос в цикле при формировании печатной формы в 1С

Программист Управленческий учет IT и автоматизация бизнеса
← На главную

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

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

Проанализируем ситуацию, описанную автором темы. Для каждой строки табличной части выполняется отдельный запрос к регистру сведений Штрихкоды. Если в документе 50 строк, система выполняет 50 обращений к базе данных. Каждое обращение — это время на установку соединения, передачу параметров, выполнение самого SQL-запроса и возврат данных. Как отметил автор, время формирования документа при 50 строках сократилось с 4 минут до 10 секунд после оптимизации. Это наглядный пример того, как архитектурно неверный подход «убивает» производительность системы.

Решение №1: Использование левого соединения в одном запросе

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

Рассмотрим пример реализации через прямое соединение с табличной частью документа:


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

Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
РезультатЗапроса = Запрос.Выполнить();

Важный момент: Мы используем ЛЕВОЕ СОЕДИНЕНИЕ. Это гарантирует, что если у товара нет штрихкода в регистре, сама строка товара все равно попадет в выборку (поле Штрихкод будет содержать NULL). Если использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ, товары без штрихкодов просто исчезнут из печатной формы.

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

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

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

  1. Создаем пакетный запрос.
  2. В первом запросе выбираем данные из табличной части документа во временную таблицу.
  3. Индексируем временную таблицу по полю Номенклатура.
  4. Во втором запросе соединяем временную таблицу с регистром сведений.

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

// Передаем таблицу товаров документа напрямую как параметр
Запрос.УстановитьПараметр("ТаблицаТоваров", СсылкаНаОбъект.Товары.Выгрузить());
РезультатЗапроса = Запрос.Выполнить();

Решение №3: Обход по группировкам (ИТОГИ)

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

Посмотрим, как организовать цикл обработки такого результата:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Товары.Номенклатура КАК Номенклатура,
    |	Штрихкоды.Штрихкод КАК Штрихкод
    |ИЗ
    |	Документ.ПриходТМЗ.Товары КАК Товары
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Штрихкоды КАК Штрихкоды
    |		ПО Товары.Номенклатура = Штрихкоды.Объект
    |ГДЕ
    |	Товары.Ссылка = &Ссылка
    |ИТОГИ ПО
    |	Номенклатура";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
ВыборкаНоменклатура = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаНоменклатура.Следующий() Цикл
    
    СтрокаШтрихкодов = "";
    ВыборкаДетали = ВыборкаНоменклатура.Выбрать();
    
    Пока ВыборкаДетали.Следующий() Цикл
        Если ЗначениеЗаполнено(ВыборкаДетали.Штрихкод) Тогда
            СтрокаШтрихкодов = СтрокаШтрихкодов + ?(ПустаяСтрока(СтрокаШтрихкодов), "", ", ") + ВыборкаДетали.Штрихкод;
        КонецЕсли;
    КонецЦикла;
    
    // Заполняем область макета
    ОбластьСтрока.Параметры.Номенклатура = ВыборкаНоменклатура.Номенклатура;
    ОбластьСтрока.Параметры.Штрихкод = СтрокаШтрихкодов;
    ТабДокумент.Вывести(ОбластьСтрока);
    
КонецЦикла;

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

Проанализируем еще несколько профессиональных приемов, которые помогут сделать код качественным:

  1. Использование БСП: Если вы работаете в современной конфигурации (УТ 11, ERP, БП 3.0), проверьте наличие общего модуля ШтрихкодированиеОбъектов. Часто в нем уже есть готовые функции типа ТаблицаШтрихкодовОбъектов(), которые работают максимально быстро и учитывают специфику (характеристики, упаковки).
  2. Избегайте отрицаний в условии ГДЕ: Условие ГДЕ НЕ Штрихкоды.Запрет может работать медленнее, чем ГДЕ Штрихкоды.Запрет = ЛОЖЬ. Для SQL-сервера поиск по конкретному значению индекса всегда предпочтительнее, чем поиск по «всему, кроме этого».
  3. Функция ЕСТЬNULL: Всегда обрабатывайте возможные пустые значения при левом соединении. В запросе конструкция ЕСТЬNULL(Штрихкоды.Штрихкод, "") позволит вам сразу получить пустую строку вместо значения NULL, что избавит от лишних проверок в коде 1С.
  4. Индексация регистра: Если даже после оптимизации кода запрос выполняется долго, выясним причину в структуре данных. Убедитесь, что в регистре Штрихкоды измерение Объект (Номенклатура) является ведущим или первым в индексе. Без этого поиск будет выполняться сканированием всей таблицы регистра.

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

← На главную