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

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

При разработке отчетов или обработок часто возникает задача получить сводные данные из табличной части документа. Например, в документе «Возврат товаров поставщику» одна и та же номенклатура может встречаться несколько раз (разные партии, характеристики или просто дубли строк). Наша цель — «схлопнуть» эти строки в одну, просуммировав количество и сумму. В этой статье мы подробно разберем, как это сделать правильно, избежав распространенных ошибок.

Ошибочный подход: Группировка во вложенном запросе

Часто разработчики пытаются получить данные, обращаясь к таблице документа (шапке) и выбирая табличную часть как вложенное поле. Выглядит это примерно так:


ВЫБРАТЬ
    Док.Ссылка,
    Док.Товары.(
        Номенклатура,
        Количество,
        Сумма
    )
ИЗ
    Документ.ВозвратТоваровПоставщику КАК Док

Ошибка заключается в попытке применить конструкцию СГРУППИРОВАТЬ ПО внутри скобок, относящихся к табличной части Док.Товары.(...), или снаружи к полю с типом «РезультатЗапроса».

Почему это не работает?

  1. Конструкция ВЫБРАТЬ Док.Товары.(...) возвращает иерархическую структуру: в каждой строке результата содержится вложенная таблица значений. Язык запросов 1С не поддерживает агрегацию (суммирование) внутри этого синтаксиса на лету.
  2. Платформа не понимает, как свернуть вложенную таблицу, пока мы находимся в контексте таблицы-шапки.

Правильный подход: Выборка из табличной части

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

Рассмотрим, как это меняет логику:

  1. Запрос становится «плоским». Каждая строка табличной части становится отдельной строкой выборки.
  2. Реквизиты шапки (Номер, Дата, Контрагент) мы получаем через обращение к ссылке: Ссылка.Контрагент.
  3. К такой плоской таблице можно применять стандартные механизмы SQL-группировки.

Шаг 1: Написание базового запроса

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


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

Шаг 2: Добавление группировки и агрегатных функций

Теперь самое важное — «схлопнуть» строки. Для этого используем конструкцию СГРУППИРОВАТЬ ПО.

Золотое правило группировки:
Если вы используете СГРУППИРОВАТЬ ПО, то все поля в секции ВЫБРАТЬ должны либо входить в секцию группировки, либо быть обернуты в агрегатную функцию (СУММА, МИНИМУМ, МАКСИМУМ, СРЕДНЕЕ, КОЛИЧЕСТВО).

Проанализируем наши поля:

Проблема с полем «Цена»

Если мы добавим поле Цена в секцию СГРУППИРОВАТЬ ПО, то строки с одинаковой номенклатурой, но разной ценой не свернутся.

Пример:
Халат, 1 шт, 100 руб.
Халат, 1 шт, 200 руб.

Если группировать по цене, в результате останется две строки, так как комбинация "Халат + 100" не равна "Халат + 200".

Решение:
Если вам нужно просто просуммировать количество и общую сумму, исключите цену из выборки или вычислите среднюю цену через выражение: СУММА(Сумма) / СУММА(Количество) (не забыв обработать деление на ноль).

Если же вы оставите поле Цена в выборке, но примените к нему функцию СУММА(Цена), вы получите сумму цен (100 + 200 = 300), что обычно не имеет экономического смысла. Чаще всего используют МАКСИМУМ(Цена) или вычисляют среднюю.

Финальный код запроса

Вот пример корректного запроса, который свернет одинаковую номенклатуру в одну строку, просуммировав количество и суммы:


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

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

Почему строки все равно не сворачиваются?

Если вы написали запрос правильно, но видите дублирующиеся строки с визуально одинаковой номенклатурой, проверим следующие причины:

  1. Разные ссылки (Дубли): В справочнике «Номенклатура» могут быть созданы два элемента с одинаковым наименованием "Халат". Для системы это разные объекты (разные УникальныйИдентификатор). Группировка идет по ссылке, а не по названию.
  2. Различия в полях группировки: Проверьте остальные поля в секции СГРУППИРОВАТЬ ПО. Например, если вы группируете по ХарактеристикаНоменклатуры, а она отличается (или в одной строке заполнена, а в другой пустая), строки не свернутся.
  3. Тип NULL: Иногда поле может казаться пустым, но в одной строке это NULL, а в другой — ПустаяСсылка. Это разные значения. Рекомендуется использовать функцию ЕСТЬNULL для полей группировки, если есть подозрение на наличие NULL.

Альтернативный метод: Свернуть в Таблице Значений

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


Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ИЗ ..."; // Запрос без группировки
ТЗ = Запрос.Выполнить().Выгрузить();

// Сворачиваем таблицу по колонкам
// Первый параметр - поля группировки (через запятую)
// Второй параметр - поля суммирования
ТЗ.Свернуть("Номенклатура, ЕдиницаИзмерения", "Количество, Сумма");

Используйте этот метод только если невозможно выполнить группировку средствами языка запросов, так как обработка данных на сервере СУБД (в запросе) всегда эффективнее.

← На главную