Как в запросе 1С сгруппировать данные по документу и получить суммы из нескольких табличных частей?

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

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

Проанализируем типичную ситуацию. У нас есть документ ПоступлениеТоваровУслуг, в котором есть табличные части "Товары" и "Услуги". Нам нужно для каждого документа вывести в одной строке общую сумму НДС по товарам и общую сумму НДС по услугам.

Начинающий разработчик может написать такой запрос:


ВЫБРАТЬ
    ПоступлениеТоваровУслуг.Ссылка КАК Ссылка,
    ПоступлениеТоваровУслуг.Контрагент КАК Контрагент1,
    ПоступлениеТоваровУслуг.СоздательДокумента КАК СоздательДокумента,
    ПоступлениеТоваровУслуг.Товары.(
        СуммаНДС КАК СуммаНДС
    ) КАК Товары,
    ПоступлениеТоваровУслуг.Услуги.(
        СуммаНДС КАК СуммаНДС
    ) КАК Услуги
ИЗ
    Документ.ПоступлениеТоваровУслуг КАК ПоступлениеТоваровУслуг
ГДЕ
    ПоступлениеТоваровУслуг.ПометкаУдаления = ЛОЖЬ
    И ПоступлениеТоваровУслуг.НДСВключенВСтоимость
    И ПоступлениеТоваровУслуг.Дата МЕЖДУ &НачалоПериода И &КонецПериода
УПОРЯДОЧИТЬ ПО
    Ссылка

Проблема этого запроса в том, что он "разворачивает" табличные части. Если в документе 5 строк в табличной части "Товары", то в результате запроса мы получим 5 строк, в каждой из которых будут повторяться данные из шапки документа (Ссылка, Контрагент и т.д.). Попытка просто добавить СГРУППИРОВАТЬ ПО Ссылка не сработает, так как в выборке есть другие неагрегированные поля. Наша цель — получить одну строку на документ с итоговыми суммами, например: Ссылка, ОбщийНДСТовары, ОбщийНДСУслуги.

Рассмотрим два эффективных способа решения этой задачи.

Подход 1: Использование подзапросов и ЛЕВОГО СОЕДИНЕНИЯ (Рекомендуемый)

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

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

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

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


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

Важные моменты:

Подход 2: Использование ОБЪЕДИНИТЬ ВСЕ во вложенном запросе

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

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

  1. Внутренний запрос. Создаем вложенный запрос, в котором с помощью оператора ОБЪЕДИНИТЬ ВСЕ мы последовательно выбираем данные из табличной части "Товары", а затем из табличной части "Услуги". Результатом будет единый список, содержащий поля Ссылка и СуммаНДС для всех строк всех табличных частей.
  2. Внешний запрос. Основной запрос обращается к результату внутреннего запроса как к виртуальной таблице. В нем мы группируем данные по полю Ссылка и суммируем все значения из колонки СуммаНДС.

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


ВЫБРАТЬ
    Данные.Ссылка КАК Ссылка,
    СУММА(Данные.СуммаНДС) КАК ОбщаяСуммаНДС
ИЗ
    (ВЫБРАТЬ
        ПоступлениеТоваровУслугТовары.Ссылка КАК Ссылка,
        ПоступлениеТоваровУслугТовары.СуммаНДС КАК СуммаНДС
    ИЗ
        Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
    ГДЕ
        ПоступлениеТоваровУслугТовары.Ссылка.ПометкаУдаления = ЛОЖЬ
        И ПоступлениеТоваровУслугТовары.Ссылка.НДСВключенВСтоимость
        И ПоступлениеТоваровУслугТовары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода

    ОБЪЕДИНИТЬ ВСЕ

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

Особенности этого подхода:

В заключение: для получения отдельных итогов из нескольких табличных частей документа метод с ЛЕВЫМ СОЕДИНЕНИЕМ и предварительной агрегацией в подзапросах является более предпочтительным, гибким и зачастую более производительным решением.

← На главную