При разработке отчетов в 1С разработчики часто сталкиваются с ошибкой: «нельзя использовать производные от полей составного типа в выражениях не присутствующих полностью в предложении СГРУППИРОВАТЬ ПО». Параллельно возникает вопрос: как правильно рассчитать итоги по количеству и сумме, если отчет строится на базе Системы компоновки данных (СКД). Разберем причины возникновения этой ошибки, выясним, почему попытка группировки внутри запроса для СКД часто является архитектурной ошибкой, и рассмотрим пошаговый алгоритм правильной настройки отчета.
Проанализируем ситуацию, когда в запросе обращаются через точку к реквизиту поля составного ссылочного типа, например, ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата, где ДокументОприходования может принимать значения различных видов документов (составной тип ДокументСсылка).
На уровне СУБД такое обращение разворачивается во множество соединений (LEFT JOIN) со всеми возможными таблицами документов конфигурации и сложное условное выражение CASE WHEN ... THEN ... END. Если в секции ВЫБРАТЬ к данному полю применяется функция (например, НАЧАЛОПЕРИОДА(..., ДЕНЬ)), а в секции СГРУППИРОВАТЬ ПО это выражение указано не полностью, платформе не удается сопоставить структуры полей в блоке агрегации, из-за чего формируется исключение.
Посмотрим на пример запроса, вызвавшего ошибку:
ВЫБРАТЬ
КурсыВалют.Курс,
КурсыВалют.Валюта,
ПартииТоваровНаСкладахОстатки.Номенклатура,
ПартииТоваровНаСкладахОстатки.Склад,
СУММА(ПартииТоваровНаСкладахОстатки.КоличествоОстаток) КАК КоличествоОстаток,
СУММА(ПартииТоваровНаСкладахОстатки.СтоимостьОстаток) КАК СтоимостьОстаток,
ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата,
НАЧАЛОПЕРИОДА(ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата, ДЕНЬ) КАК Поле1,
КурсыВалютСрезПоследних.Курс КАК Курс1,
СУММА(ВЫРАЗИТЬ(ВЫБОР
КОГДА ПартииТоваровНаСкладахОстатки.Номенклатура.Валютный = ИСТИНА
ТОГДА ПартииТоваровНаСкладахОстатки.СтоимостьОстаток / КурсыВалют.Курс * КурсыВалютСрезПоследних.Курс
ИНАЧЕ ПартииТоваровНаСкладахОстатки.СтоимостьОстаток
КОНЕЦ КАК ЧИСЛО(15, 2))) КАК КорректнаяСтоимость,
ПартииТоваровНаСкладахОстатки.ДокументОприходования
ИЗ
РегистрНакопления.ПартииТоваровНаСкладах.Остатки(&Дата1, ) КАК ПартииТоваровНаСкладахОстатки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
ПО (НАЧАЛОПЕРИОДА(ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата, ДЕНЬ) = КурсыВалют.Период),
РегистрСведений.КурсыВалют.СрезПоследних(&Дата1, ) КАК КурсыВалютСрезПоследних
ГДЕ
КурсыВалют.Валюта.Код = "840"
И КурсыВалютСрезПоследних.Валюта.Код = "840"
СГРУППИРОВАТЬ ПО
КурсыВалют.Курс,
КурсыВалют.Валюта,
ПартииТоваровНаСкладахОстатки.Номенклатура,
ПартииТоваровНаСкладахОстатки.Склад,
ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата,
КурсыВалютСрезПоследних.Курс,
ПартииТоваровНаСкладахОстатки.ДокументОприходования
В данном предложении группировки отсутствует выражение НАЧАЛОПЕРИОДА(ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата, ДЕНЬ), что и вызвало синтаксическую ошибку платформы 1С.
Если запрос используется в коде без Системы компоновки данных и группировка действительно необходима, исправить проблему можно следующими способами:
СГРУППИРОВАТЬ ПО: включим НАЧАЛОПЕРИОДА(ПартииТоваровНаСкладахОстатки.ДокументОприходования.Дата, ДЕНЬ) в список группировочных полей.ВЫРАЗИТЬ(ПартииТоваровНаСкладахОстатки.ДокументОприходования КАК Документ.ПоступлениеТоваровУслуг).Дата. Это избавит СУБД от генерации множественных соединений.Когда отчет разрабатывается с использованием Системы компоновки данных (СКД), применение СГРУППИРОВАТЬ ПО и агрегатных функций (СУММА, КОЛИЧЕСТВО) в самом тексте запроса является ошибкой. Агрегация в запросе «схлопывает» детальные записи и лишает СКД возможности динамически рассчитывать промежуточные итоги, строить иерархию папок номенклатуры и вычислять общие суммы.
Разберем по шагам, как правильно организовать получение итогов.
СУММА() и блок СГРУППИРОВАТЬ ПО.КоличествоОстаток, СтоимостьОстаток, КорректнаяСтоимость). В колонке «Выражение» укажем стандартную агрегатную функцию: Сумма(КоличествоОстаток).Номенклатура и укажем тип группировки: «Иерархия» (или «Только иерархия» / «Элементы» в зависимости от задачи). Внутрь группировки по номенклатуре при необходимости вложим детальные записи.Рассмотрим дополнительные моменты, влияющие на корректность и быстродействие запроса:
ГДЕ. Отборы по номенклатуре, складам или типам цен необходимо передавать напрямую во второй параметр виртуальной таблицы РегистрНакопления.ПартииТоваровНаСкладах.Остатки(&Дата1, [Условие]).РегистрСведений.КурсыВалют по точному равенству даты может привести к потере строк остатков, если на дату документа оприходования курс не вводился (например, в выходные или праздничные дни). Для получения курсов на исторические даты безопаснее использовать срез последних на каждую дату документа через временные таблицы либо вычисляемые поля.Стоимость * Кратность / Курс), чтобы избежать искажения данных для валют с нестандартной кратностью.