Как правильно писать запросы для печатных форм в 1С: выборка шапки, табличной части и контактной информации

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке внешних печатных форм (например, расходной накладной, ТОРГ-12 или транспортной накладной) перед разработчиком встает ключевая задача: как наиболее оптимально и чисто получить данные шапки документа, его табличной части и сопутствующих сведений (контактной информации контрагентов, адресов погрузки и разгрузки). Разберем типовые подходы к формированию запросов, сравним их производительность и выделим лучшие практики платформы 1С:Предприятие.

Вариант 1. Разыменование полей через точку от табличной части

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

Посмотрим на пример такого запроса:


ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
    РеализацияТоваровУслугТовары.Ссылка.Номер КАК Номер,
    РеализацияТоваровУслугТовары.Ссылка.Дата КАК Дата,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    РеализацияТоваровУслугТовары.Количество КАК Количество
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Док

Проанализируем работу этого механизма. Существует распространенное заблуждение, будто обращение Ссылка.Номер порождает отдельный подзапрос на каждую строку. В действительности платформа 1С автоматически преобразует такое разыменование во внутреннее соединение с таблицей документа на уровне СУБД. Это абсолютно валидный и компактный вариант, если документ содержит строки табличной части. Однако если табличная часть окажется пустой, запрос вернет нулевой результат, и данные шапки получены не будут.

Вариант 2. Пакетные запросы: разделение шапки и табличной части

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

Посмотрим на структуру пакетного запроса:


ВЫБРАТЬ
    РеализацияТоваровУслуг.Ссылка КАК Ссылка,
    РеализацияТоваровУслуг.Номер КАК Номер,
    РеализацияТоваровУслуг.Дата КАК Дата,
    РеализацияТоваровУслуг.Контрагент КАК Контрагент
ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
    РеализацияТоваровУслуг.Ссылка = &Док
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    РеализацияТоваровУслугТовары.Количество КАК Количество
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Док

Разберем по шагам, как правильно выполнить и обработать данный пакет на встроенном языке 1С:

  1. Вызываем метод ВыполнитьПакет() у объекта Запрос, который возвращает массив результатов выполнения каждого запроса в порядке их следования.
  2. Извлекаем выборку шапки из первого элемента массива (индекс 0) и заполняем параметры печатной формы.
  3. Извлекаем выборку табличной части из второго элемента массива (индекс 1) и выводим строки табличного документа в цикле.

Посмотрим на программную реализацию обработки пакета:


МассивРезультатов = Запрос.ВыполнитьПакет();

// Обработка данных шапки
ВыборкаШапка = МассивРезультатов[0].Выбрать();
Если ВыборкаШапка.Следующий() Тогда
    // Заполняем область шапки
КонецЕсли;

// Обработка строк табличной части
ВыборкаСтроки = МассивРезультатов[1].Выбрать();
Пока ВыборкаСтроки.Следующий() Цикл
    // Выводим детальные строки
КонецЦикла;

Вариант 3. Использование группировок с предложением «ИТОГИ ПО»

Еще один профессиональный способ объединить шапку и строки без лишних дублей при обходе — использование конструкции ИТОГИ ПО. В этом случае запрос связывает таблицы, а иерархия выборки берет управление на себя:


ВЫБРАТЬ
    РеализацияТоваровУслуг.Ссылка КАК Ссылка,
    РеализацияТоваровУслуг.Номер КАК Номер,
    РеализацияТоваровУслуг.Дата КАК Дата,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    РеализацияТоваровУслугТовары.Количество КАК Количество
ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ПО РеализацияТоваровУслуг.Ссылка = РеализацияТоваровУслугТовары.Ссылка
ГДЕ
    РеализацияТоваровУслуг.Ссылка = &Док
ИТОГИ ПО
    Ссылка

Для обхода такого результата применяем специальный режим:


ВыборкаДокумент = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаДокумент.Следующий() Цикл
    // Доступны реквизиты шапки (Номер, Дата) без дублирования
    
    ВыборкаСтроки = ВыборкаДокумент.Выбрать();
    Пока ВыборкаСтроки.Следующий() Цикл
        // Доступны строки табличной части (Номенклатура, Количество)
    КонецЦикла;
КонецЦикла;

Почему не стоит выбирать табличные части через синтаксис со скобками

Платформа позволяет обращаться к табличным частям через синтаксис Документ.Товары.(Номенклатура, Количество). Однако использовать эту конструкцию в реальных задачах не рекомендуется. При таком подходе СУБД формирует отдельную таблицу значений на каждую строку выборки, что приводит к избыточному потреблению оперативной памяти. Кроме того, такие вложенные таблицы нельзя помещать во временные таблицы (ПОМЕСТИТЬ) и объединять через ОБЪЕДИНИТЬ.

Оптимизация получения контактной информации и связанных данных

Выясним причину частых падений производительности при выводе адресов и контактных данных. Грубой ошибкой является вызов функций с запросами внутри цикла или предварительное чтение объекта через Ссылка.ПолучитьОбъект(). Метод ПолучитьОбъект() считывает из базы абсолютно все реквизиты, табличные части и устанавливает контекст блокировок, что для печатных форм недопустимо.

Все сопутствующие реквизиты (адрес организации, адрес склада, пункт доставки контрагента) необходимо получать в общем запросе с помощью левых соединений. Посмотрим, как связать данные документа с регистром сведений контактной информации:


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

Важные правила при работе со связями контактной информации:

  1. Не фильтруйте по наименованию вида КИ через строку: Условие вида Вид.Наименование = "Юридический адрес" замедляет выполнение запроса и приводит к ошибкам, если пользователь изменит название вида в пользовательском режиме. Передавайте ссылку на вид контактной информации параметром (или используйте предопределенные значения через конструкцию ЗНАЧЕНИЕ(...)).
  2. Один запрос вместо нескольких вызовов: Несколько левых соединений в одном запросе выполняются на сервере базы данных несоизмеримо быстрее, чем серия мелких отдельных обращений к базе.
  3. Готовность к пакетной печати: Всегда проектируйте логику с расчетом на то, что в параметре &Док может быть передан не один документ, а массив ссылок (условие ГДЕ Ссылка В (&МассивДокументов)). Подход с пакетным запросом или группировкой ИТОГИ ПО идеально масштабируется на пакетную печать.
← На главную