Почему запрос 1С считает СУММУ вместо СРЕДНЕГО в итогах?

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

При работе с языком запросов 1С:Предприятие программисты часто сталкиваются с ситуацией, когда использование агрегатной функции СРЕДНЕЕ в секции ИТОГИ выдает результат, подозрительно похожий на обычную сумму. Особенно часто это проявляется при работе с виртуальными таблицами регистров накопления и использованием конструкций дополнения периодов. В рамках этой статьи мы разберем, почему так происходит, проанализируем механизм расчета итогов и выясним, как получить корректные данные.

Анализ исходной проблемы

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


ВЫБРАТЬ
    CRM_ПродажиОбороты.ПериодМесяц КАК ПериодМесяц,
    CRM_ПродажиОбороты.ПериодГод КАК ПериодГод,
    CRM_ПродажиОбороты.КоличествоОборот КАК КоличествоОборот,
    ЕстьNULL(CRM_ПродажиОбороты.ЕЦГ_ТарифОборот, 0) КАК ЕЦГ_ТарифОборот
ИЗ
    РегистрНакопления.CRM_Продажи.Обороты(&ДатаНач, &ДатаКон, Авто, Партнер В (&Партнеры)) КАК CRM_ПродажиОбороты
ИТОГИ
    СРЕДНЕЕ(КоличествоОборот),
    СРЕДНЕЕ(ЕЦГ_ТарифОборот)
ПО
    ПериодГод ПЕРИОДАМИ(МЕСЯЦ, , ),
    ПериодМесяц

В данном случае для ресурса КоличествоОборот расчет идет верно, а для поля ЕЦГ_ТарифОборот результат совпадает с суммой. Выясним причину такого поведения системы.

Особенность 1: Агрегация в виртуальных таблицах

Первое, что мы должны учитывать: виртуальная таблица Обороты — это уже агрегированные данные. В отличие от физической таблицы (таблицы движений), где каждая запись соответствует документу, в виртуальной таблице данные сгруппированы по измерениям и выбранному периоду (в нашем случае — Авто, что в контексте ПериодМесяц означает группировку по месяцам).

Когда мы применяем СРЕДНЕЕ в секции ИТОГИ к полям виртуальной таблицы, система рассчитывает среднее арифметическое от **уже просуммированных** значений. Проанализируем разницу:

  1. Физическая таблица: Если за месяц было 10 продаж по 100 рублей, среднее будет (100+100...+100) / 10 = 100.
  2. Виртуальная таблица (за месяц): Система видит одну запись с итогом 1000 рублей. Если мы просим среднее по году, где был оборот только в этом месяце, система видит: 1000 / 1 (строку) = 1000.

Именно поэтому результат кажется "суммой" — если в периоде группировки (например, в году) только один месяц с данными, среднее от одного значения будет равно этому самому значению (сумме за месяц).

Особенность 2: Влияние конструкции ПЕРИОДАМИ

Конструкция ПЕРИОДАМИ(МЕСЯЦ, , ) критически влияет на расчет среднего. Она заставляет платформу 1С "дорисовывать" в выборке недостающие месяцы, по которым не было движений. Рассмотрим, как это меняет математику процесса:

Если в году продажи были только в марте (сумма 120), то:

Многие разработчики ошибочно ожидают, что система будет игнорировать пустые месяцы при расчете среднего, но механизм ИТОГИ считает количество строк в сформированной выборке. Если ПЕРИОДАМИ добавило строки с нулевыми значениями, они станут полноценными участниками знаменателя в формуле среднего арифметического.

Разбор ситуации с NULL в ресурсах

В процессе обсуждения часто возникает вопрос о наличии значений NULL в числовых ресурсах. Рассмотрим этот момент подробнее. В платформе 1С ресурсы регистров накопления имеют числовой тип и физически в базе данных хранятся как числа (по умолчанию 0). Значение NULL не может храниться в ресурсе регистра накопления.

Однако NULL может появиться в результате работы запроса:

Важно помнить: функция СРЕДНЕЕ в SQL (и в 1С) игнорирует NULL при расчете знаменателя, но учитывает значение 0. Если мы используем ЕстьNULL(Тариф, 0), мы принудительно превращаем отсутствующие записи в нули. В результате знаменатель растет, а сумма остается прежней, что резко занижает итоговое среднее значение.

Сравнение с запросом к физической таблице

Чтобы убедиться, что проблема не в данных, а в методе обращения к ним, проанализируем запрос к реальным записям регистра (физической таблице):


ВЫБРАТЬ
    CRM_Продажи.Партнер КАК Партнер,
    СУММА(CRM_Продажи.ЕЦГ_Тариф) КАК ЕЦГ_Тариф,
    СРЕДНЕЕ(CRM_Продажи.ЕЦГ_Тариф) КАК ЕЦГ_Тариф1,
    СУММА(CRM_Продажи.Количество) КАК Количество,
    СРЕДНЕЕ(CRM_Продажи.Количество) КАК Количество1
ИЗ
    РегистрНакопления.CRM_Продажи КАК CRM_Продажи
ГДЕ
    CRM_Продажи.Партнер = &Партнер
    И CRM_Продажи.Период >= &ДатаНач
    И CRM_Продажи.Период <= &ДатаКон
СГРУППИРОВАТЬ ПО
    CRM_Продажи.Партнер

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

Рекомендации по решению проблемы

Для получения корректного среднего значения в отчетах рекомендуется придерживаться следующих правил:

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

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

← На главную