При работе с отчетами на Системе Компоновки Данных (СКД) в 1С иногда возникает ситуация, когда агрегированные данные (например, сумма количества) отображаются некорректно. Отчет как будто "схлопывает" несколько одинаковых записей в одну, даже если в исходной таблице они являются разными. Давайте разберем эту проблему на примере и предложим комплексные решения.
Представим, что у нас есть следующая исходная таблица с информацией о заказанной продукции:
Заказ Номенклатура Дата Кол
Зак1 Н1 Д1 3
Зак2 Н1 Д1 3
Зак3 Н1 Д1 2
Мы хотим получить отчет, который будет суммировать количество продукции по номенклатуре, а в детальных записях показывать дату и количество. Если мы строим отчет с верхней группировкой по Номенклатуре и детальными записями по Дате и Количеству, то можем столкнуться со следующим некорректным результатом:
Номенкл Кол
Н1 5
Д1 3
Д1 2
В этом случае вместо ожидаемой суммы в 8 единиц по номенклатуре Н1, мы получаем 5. Система, по всей видимости, посчитала две записи с "Н1" и "Д1" как одну и просуммировала их количества. При этом, если мы добавляем поле "Заказ" в детальные записи отчета, результат становится правильным:
Номенкл Кол
Н1 8
Зак1 Д1 3
Зак2 Д1 3
Зак3 Д1 2
Проблема заключается в том, что СКД, при отсутствии поля "Заказ" в детальной группировке, "схлопывает" строки с одинаковыми "Номенклатура" и "Дата", что приводит к неправильной сумме. Пользователь не хочет выводить поле "Заказ" в отчете, но оно необходимо для корректного суммирования. Разберем, почему так происходит и как этого избежать.
Чтобы эффективно решить эту проблему, нам необходимо понимать, как Система Компоновки Данных формирует свои внутренние запросы и определяет уникальность строк для группировки и агрегации. СКД по умолчанию строит SQL-запрос, который включает в себя операторы SELECT и GROUP BY. Список полей в секции GROUP BY формируется на основе полей, которые мы указали в группировках отчета (включая детальные записи).
Если набор полей, указанных в детальных записях отчета (например, только Дата и Количество), не является уникальным для каждой исходной строки данных, СКД воспримет эти "идентичные" по отображаемым полям строки как одну и произведет агрегацию (суммирование, например) для них. В нашем примере, когда мы выводим только Дата и Количество, СКД видит две строки: "Д1, 3" и "Д1, 2". Если бы в источнике было две строки "Д1, 3" и "Д1, 3", СКД могла бы их "схлопнуть" в одну запись "Д1, 3", если нет других уникальных полей, что приведет к потере одной из исходных записей при суммировании.
Когда мы добавляем поле Заказ в детальные записи, даже если значения полей Номенклатура и Дата совпадают, наличие уникального Заказ (Зак1, Зак2, Зак3) делает каждую строку уникальной для СКД, и она корректно суммирует все исходные количества. Задача состоит в том, чтобы "сообщить" СКД о существовании уникального поля, не выводя его при этом в отчете.
Это наиболее распространенное, эффективное и рекомендуемое решение для описанной проблемы. Оно позволяет СКД корректно суммировать все строки, даже если по выбранным для отображения полям они кажутся одинаковыми.
Суть метода: Мы явно указываем СКД, какое поле обеспечивает уникальность каждой исходной записи. Это поле будет включено во внутренний запрос СКД для группировки детальных записей, но при этом мы скроем его отображение в итоговом отчете. Таким образом, СКД будет "видеть" каждую строку как уникальную и выполнять корректную агрегацию, а пользователю это поле не будет мешать.
Разберем по шагам, как это реализовать:
Заказ (или любое другое поле, которое однозначно идентифицирует каждую исходную строку, например, уникальный идентификатор строки, если таковой имеется в источнике данных) из списка "Доступные поля" (обычно слева) в список полей для детальных записей. Это можно сделать, перетащив его в раздел "Выбранные поля" для детальных записей, или добавив его через контекстное меню.Заказ в детальные записи, выделим его в списке полей. В правой части окна настроек СКД откроются свойства выбранного поля.Заказ будет использоваться СКД для построения запроса и обеспечения уникальности строк, но при этом не будет отображаться в сформированном отчете.Как это работает? Когда мы устанавливаем для поля Заказ свойство "Вывод" в "Не выводить", СКД при формировании своего внутреннего SQL-запроса включает это поле в секцию GROUP BY для детальных записей, даже если оно не отображается. Таким образом, СКД "видит", что записи Зак1, Зак2, Зак3 являются уникальными по полю Заказ, хотя их Номенклатура и Дата могут совпадать. Это предотвращает нежелательное схлопывание строк и обеспечивает корректное суммирование количества для каждой отдельной записи заказа, а затем агрегацию этих сумм до верхних уровней группировок без потерь.
Этот подход является менее прямым, чем предыдущий, но в некоторых ситуациях может помочь "подсказать" СКД, что определенное поле является ключевым и должно всегда присутствовать во внутренних запросах, даже если оно не используется напрямую в группировках или детальных записях.
Принцип действия: Установка роли для поля в наборе данных СКД изменяет его поведение при формировании запросов. Роль "Обязательное" или "Идентификатор" может заставить СКД включать это поле в промежуточные запросы, которые формируются системой, предотвращая его нежелательное исключение и, как следствие, потерю уникальности строк на ранних этапах обработки данных.
Разберем по шагам, как это сделать:
Заказ в списке полей.Важно: Этот метод может не всегда работать с той же предсказуемостью, как прямое добавление поля в детальные записи с сокрытием вывода. Его стоит рассмотреть как дополнительную меру или альтернативу, если первый метод по каким-то причинам не принес желаемого результата или если поле действительно играет роль системного идентификатора.
Иногда корень проблемы лежит глубже, чем настройки СКД, и может быть связан с тем, как данные уже агрегируются на уровне самого запроса, который формирует набор данных для СКД. Это особенно актуально, если в запросе используются временные таблицы.
Причина возникновения: Если во временной таблице уже происходит агрегация данных (например, с использованием оператора СГРУППИРОВАТЬ ПО), и из списка полей группировки было исключено поле, которое гарантирует уникальность (в нашем случае Заказ), то СКД получит уже "схлопнутые" данные. В такой ситуации, никакие настройки СКД в отчете не смогут восстановить потерянную на более раннем этапе детализацию, так как уникальная информация уже была агрегирована и потеряна.
Что нужно проверить:
РАЗЛИЧНЫЕ (DISTINCT), которые могут преждевременно удалить дубликаты записей. СКД обычно сама формирует оптимальный запрос с учетом группировок, и преждевременное использование РАЗЛИЧНЫЕ может нарушить ее логику.Заказ не включено в СГРУППИРОВАТЬ ПО, это может привести к потере уникальности.Пример некорректного запроса во временной таблице, ведущего к потере детализации:
Представим, что у нас есть исходная таблица `ТаблицаЗаказов`, и мы создаем временную таблицу `ВТ_АгрегированныеДанные`, пытаясь агрегировать данные:
ВЫБРАТЬ
ТаблицаЗаказов.Номенклатура КАК Номенклатура,
ТаблицаЗаказов.Дата КАК Дата,
СУММА(ТаблицаЗаказов.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_АгрегированныеДанные
ИЗ
ТаблицаЗаказов КАК ТаблицаЗаказов
СГРУППИРОВАТЬ ПО
ТаблицаЗаказов.Номенклатура,
ТаблицаЗаказов.Дата
;
// Дальнейшая работа с ВТ_АгрегированныеДанные
ВЫБРАТЬ
ВТ_АгрегированныеДанные.Номенклатура,
ВТ_АгрегированныеДанные.Дата,
ВТ_АгрегированныеДанные.Количество
ИЗ
ВТ_АгрегированныеДанные КАК ВТ_АгрегированныеДанные
В этом примере, если поле Заказ не было включено в СГРУППИРОВАТЬ ПО при создании ВТ_АгрегированныеДанные, то все записи с одинаковой Номенклатурой и Датой будут схлопнуты, и информация о конкретных заказах (Зак1, Зак2, Зак3) будет потеряна. В результате, даже если мы затем попытаемся добавить Заказ в настройки СКД, его уже не будет в детальном виде.
Как исправить ситуацию с временными таблицами:
Если нам необходимо сохранить полную детализацию, исходный запрос должен предоставлять СКД "сырые" данные без предварительной агрегации, которую должна выполнять сама СКД. То есть, во временных таблицах мы должны выбирать все поля, включая уникальный идентификатор (Заказ), без оператора СГРУППИРОВАТЬ ПО, если целью является передача максимально подробных данных для дальнейшей обработки СКД:
ВЫБРАТЬ
ТаблицаЗаказов.Заказ КАК Заказ,
ТаблицаЗаказов.Номенклатура КАК Номенклатура,
ТаблицаЗаказов.Дата КАК Дата,
ТаблицаЗаказов.Количество КАК Количество
ПОМЕСТИТЬ ВТ_ИсходныеДанные
ИЗ
ТаблицаЗаказов КАК ТаблицаЗаказов
;
// Далее СКД будет работать с этой ВТ_ИсходныеДанные
ВЫБРАТЬ
ВТ_ИсходныеДанные.Заказ,
ВТ_ИсходныеДанные.Номенклатура,
ВТ_ИсходныеДанные.Дата,
ВТ_ИсходныеДанные.Количество
ИЗ
ВТ_ИсходныеДанные КАК ВТ_ИсходныеДанные
В этом случае СКД получит все исходные данные, включая поле Заказ, и сможет корректно выполнить агрегацию в соответствии с настройками отчета (например, согласно Решению 1).
Если же агрегация во временной таблице все-таки необходима, но нужно сохранить возможность детализации по Заказ, то Заказ должен быть обязательно включен в СГРУППИРОВАТЬ ПО в этой временной таблице:
ВЫБРАТЬ
ТаблицаЗаказов.Заказ КАК Заказ,
ТаблицаЗаказов.Номенклатура КАК Номенклатура,
ТаблицаЗаказов.Дата КАК Дата,
СУММА(ТаблицаЗаказов.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_АгрегированныеДанныеПоЗаказу
ИЗ
ТаблицаЗаказов КАК ТаблицаЗаказов
СГРУППИРОВАТЬ ПО
ТаблицаЗаказов.Заказ,
ТаблицаЗаказов.Номенклатура,
ТаблицаЗаказов.Дата
;
// Дальнейшая работа с ВТ_АгрегированныеДанныеПоЗаказу
ВЫБРАТЬ
ВТ_АгрегированныеДанныеПоЗаказу.Заказ,
ВТ_АгрегированныеДанныеПоЗаказу.Номенклатура,
ВТ_АгрегированныеДанныеПоЗаказу.Дата,
ВТ_АгрегированныеДанныеПоЗаказу.Количество
ИЗ
ВТ_АгрегированныеДанныеПоЗаказу КАК ВТ_АгрегированныеДанныеПоЗаказу
Функция ВычислитьВыражение() в ресурсах СКД предоставляет большую гибкость для выполнения сложных и нестандартных вычислений. В некоторых случаях ее можно использовать для принудительной агрегации с учетом определенного поля, даже если оно не присутствует в явном виде в детальных записях для вывода.
Применение: Вместо того чтобы просто указывать Сумма(Количество) в ресурсах отчета, мы можем попробовать использовать более сложное выражение, которое явно указывает СКД контекст агрегации.
Разберем, как это можно сделать:
Количество), изменим его выражение.Сумма(Количество) укажем:
ВычислитьВыражение("Сумма(Количество)", "Заказ")
Объяснение: Данное выражение указывает СКД, что суммирование поля Количество должно производиться в контексте каждой уникальной записи по полю Заказ. Параметр "Заказ" во второй части функции ВычислитьВыражение() указывает на поле, по которому должна производиться детализация для вычисления внутренней суммы. Это может заставить СКД включить Заказ во внутренние группировки для расчета данного ресурса, тем самым предотвратив схлопывание.
Важно: Для простых случаев суммирования этот метод часто является избыточным и может указывать на то, что базовая проблема отсутствия уникального идентификатора на уровне детальных записей не решена оптимальным образом. Всегда предпочтительнее сначала использовать методы, описанные в Решении 1, поскольку они более прозрачны, соответствуют принципам работы СКД и проще в поддержке. ВычислитьВыражение() лучше оставлять для более сложных вычислений, которые нельзя решить стандартными средствами агрегации.
В заключение, тщательное понимание того, как СКД формирует свои внутренние запросы и как она определяет уникальность записей для агрегации, является ключом к решению подобных проблем. В большинстве случаев достаточно просто добавить уникальный идентификатор (например, номер заказа) в детальные записи отчета и скрыть его вывод, чтобы СКД корректно обрабатывала все строки и предоставляла точные агрегированные данные.