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