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