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