Одной из самых распространенных ошибок начинающих разработчиков 1С является выполнение запроса внутри цикла. В контексте формирования печатных форм это приводит к катастрофическому падению производительности: документ, который должен формироваться доли секунды, может «зависать» на несколько минут. Разберем, почему это происходит и как переписать такой код правильно, используя современные стандарты разработки.
Проанализируем ситуацию, описанную автором темы. Для каждой строки табличной части выполняется отдельный запрос к регистру сведений Штрихкоды. Если в документе 50 строк, система выполняет 50 обращений к базе данных. Каждое обращение — это время на установку соединения, передачу параметров, выполнение самого SQL-запроса и возврат данных. Как отметил автор, время формирования документа при 50 строках сократилось с 4 минут до 10 секунд после оптимизации. Это наглядный пример того, как архитектурно неверный подход «убивает» производительность системы.
Самый простой и эффективный способ исправить ситуацию — получить все необходимые данные одним запросом. Вместо того чтобы перебирать строки табличной части в коде 1С, мы попросим саму базу данных сопоставить товары из документа с их штрихкодами.
Рассмотрим пример реализации через прямое соединение с табличной частью документа:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыДокумента.Номенклатура КАК Номенклатура,
| ТоварыДокумента.Количество КАК Количество,
| Штрихкоды.Штрихкод КАК Штрихкод
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК ТоварыДокумента
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Штрихкоды КАК Штрихкоды
| ПО ТоварыДокумента.Номенклатура = Штрихкоды.Объект
|ГДЕ
| ТоварыДокумента.Ссылка = &Ссылка
| И (НЕ Штрихкоды.Запрет ИЛИ Штрихкоды.Запрет ЕСТЬ NULL)";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
РезультатЗапроса = Запрос.Выполнить();
Важный момент: Мы используем ЛЕВОЕ СОЕДИНЕНИЕ. Это гарантирует, что если у товара нет штрихкода в регистре, сама строка товара все равно попадет в выборку (поле Штрихкод будет содержать NULL). Если использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ, товары без штрихкодов просто исчезнут из печатной формы.
Для повышения надежности и скорости (особенно в крупных конфигурациях) рекомендуется сначала выгрузить данные табличной части во временную таблицу, проиндексировать её, а затем соединять с регистром. Это позволяет избежать проблем с производительностью при работе с очень большими табличными частями.
Разберем алгоритм по шагам:
Номенклатура.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| Товары.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| &ТаблицаТоваров КАК Товары
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|ВЫБРАТЬ
| ВТ_Товары.Номенклатура КАК Номенклатура,
| ВТ_Товары.Количество КАК Количество,
| ЕСТЬNULL(Штрихкоды.Штрихкод, """") КАК Штрихкод
|ИЗ
| ВТ_Товары КАК ВТ_Товары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Штрихкоды КАК Штрихкоды
| ПО ВТ_Товары.Номенклатура = Штрихкоды.Объект
|ГДЕ
| НЕ Штрихкоды.Запрет";
// Передаем таблицу товаров документа напрямую как параметр
Запрос.УстановитьПараметр("ТаблицаТоваров", СсылкаНаОбъект.Товары.Выгрузить());
РезультатЗапроса = Запрос.Выполнить();
Если у одной номенклатуры может быть несколько штрихкодов, и их нужно вывести через запятую в одной ячейке, лучше всего использовать итоги в запросе. Это позволит нам удобно сгруппировать данные еще на уровне выборки.
Посмотрим, как организовать цикл обработки такого результата:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| Штрихкоды.Штрихкод КАК Штрихкод
|ИЗ
| Документ.ПриходТМЗ.Товары КАК Товары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Штрихкоды КАК Штрихкоды
| ПО Товары.Номенклатура = Штрихкоды.Объект
|ГДЕ
| Товары.Ссылка = &Ссылка
|ИТОГИ ПО
| Номенклатура";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
ВыборкаНоменклатура = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаНоменклатура.Следующий() Цикл
СтрокаШтрихкодов = "";
ВыборкаДетали = ВыборкаНоменклатура.Выбрать();
Пока ВыборкаДетали.Следующий() Цикл
Если ЗначениеЗаполнено(ВыборкаДетали.Штрихкод) Тогда
СтрокаШтрихкодов = СтрокаШтрихкодов + ?(ПустаяСтрока(СтрокаШтрихкодов), "", ", ") + ВыборкаДетали.Штрихкод;
КонецЕсли;
КонецЦикла;
// Заполняем область макета
ОбластьСтрока.Параметры.Номенклатура = ВыборкаНоменклатура.Номенклатура;
ОбластьСтрока.Параметры.Штрихкод = СтрокаШтрихкодов;
ТабДокумент.Вывести(ОбластьСтрока);
КонецЦикла;
Проанализируем еще несколько профессиональных приемов, которые помогут сделать код качественным:
ШтрихкодированиеОбъектов. Часто в нем уже есть готовые функции типа ТаблицаШтрихкодовОбъектов(), которые работают максимально быстро и учитывают специфику (характеристики, упаковки).ГДЕ НЕ Штрихкоды.Запрет может работать медленнее, чем ГДЕ Штрихкоды.Запрет = ЛОЖЬ. Для SQL-сервера поиск по конкретному значению индекса всегда предпочтительнее, чем поиск по «всему, кроме этого».ЕСТЬNULL(Штрихкоды.Штрихкод, "") позволит вам сразу получить пустую строку вместо значения NULL, что избавит от лишних проверок в коде 1С.Штрихкоды измерение Объект (Номенклатура) является ведущим или первым в индексе. Без этого поиск будет выполняться сканированием всей таблицы регистра.Применяя эти подходы, мы переходим от «наивного» программирования к профессиональной разработке, обеспечивая пользователям комфортную и быструю работу с программой 1С.