Как рассчитать среднее значение нарастающим итогом в отчете 1С

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

При построении аналитических отчетов часто возникает задача вычисления среднего значения не просто за период, а динамически — на каждый конкретный день. Это позволяет видеть тренд изменения показателей, например, среднего количества работающих сотрудников или договорников ГПХ (ДГПХ) с начала месяца. В этой статье мы разберем несколько способов реализации этой задачи: от простого изменения программного кода до использования мощных инструментов системы компоновки данных (СКД).

Постановка задачи

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

Решение 1: Расчет внутри цикла при выводе отчета

Если ваш отчет строится программно с использованием объекта ТаблицаЗначений и последующим выводом в ТабличныйДокумент, то самым быстрым способом будет внедрение переменных-накопителей непосредственно в цикл обхода данных. Рассмотрим, как это реализовать на практике.

Для начала нам понадобятся две переменные: одна для хранения накопленной суммы (числитель), другая — для хранения количества прошедших дней или строк (знаменатель). Разберем по шагам алгоритм изменений в коде:


// Инициализируем переменные перед началом цикла
СуммаНарастающимИтогом = 0;
ВсегоЗначений = 0;

Для Каждого СтрокаТЗ Из ТЗ Цикл
    
    // Получаем значение текущего дня
    ТекущееКоличество = СтрокаТЗ.ДГПХ;
    
    // Накапливаем сумму и увеличиваем счетчик строк
    СуммаНарастающимИтогом = СуммаНарастающимИтогом + ТекущееКоличество;
    ВсегоЗначений = ВсегоЗначений + 1;
    
    // Рассчитываем среднее значение
    СреднееЗначение = СуммаНарастающимИтогом / ВсегоЗначений;
    
    // Выводим данные в параметры макета
    СтрокаТЧ = Макет.ПолучитьОбласть("СтрокаТЧ");
    СтрокаТЧ.Параметры.День = СтрокаТЗ.День;
    СтрокаТЧ.Параметры.ДГПХ = ТекущееКоличество;
    СтрокаТЧ.Параметры.Среднее = СреднееЗначение; // Добавляем новый параметр
    
    НовТабДок.Вывести(СтрокаТЧ);
    
КонецЦикла;

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

Решение 2: Использование СКД (Система компоновки данных)

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

Рассмотрим подробнее, как настроить расчет среднего нарастающим итогом в схеме компоновки:

  1. Перейдите на вкладку "Вычисляемые поля" и добавьте новое поле, например, СреднееНарастающим.
  2. На вкладке "Ресурсы" добавьте это поле. В колонке "Выражение" пропишите формулу:

    Сумма(ДГПХ) / Количество(Период)

  3. Для того чтобы расчет шел именно нарастающим итогом, используется более сложная конструкция функции ВычислитьВыражение. Она позволяет суммировать данные от первой строки до текущей:

    ВычислитьВыражение("Сумма(ДГПХ)", , , "Первая", "Текущая") / ВычислитьВыражение("Количество(Период)", , , "Первая", "Текущая")

Этот метод позволяет избежать написания кода на встроенном языке и предоставляет пользователю возможность настраивать группировки отчета самостоятельно.

Решение 3: Оптимизация получения данных через запрос

Проанализируем ситуацию, описанную в исходном сообщении форума. Автор использует функцию ПодсчетДоговорников(ТекДат) внутри цикла. Это классическая ошибка производительности — так называемый "запрос в цикле". Если период отчета составит год (365 дней), система выполнит 365 отдельных обращений к базе данных.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Договоры.ДатаНачала КАК Период,
    |   КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Договоры.Ссылка) КАК Количество
    |ИЗ
    |   Документ.ДоговорГПХ КАК Договоры
    |ГДЕ
    |   Договоры.ДатаНачала МЕЖДУ &Дата1 И &Дата2
    |СГРУППИРОВАТЬ ПО
    |   Договоры.ДатаНачала";

Запрос.УстановитьПараметр("Дата1", Отчет.Дата1);
Запрос.УстановитьПараметр("Дата2", Отчет.Дата2);

ТЗ_Результат = Запрос.Выполнить().Выгрузить();

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

Методологические особенности расчета

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

Резюме: Для быстрого разового отчета используйте счетчики в цикле (как в Решении 1). Для профессиональной разработки в типовых конфигурациях используйте СКД с функцией ВычислитьВыражение. И всегда старайтесь избегать вызова функций получения данных внутри циклов, чтобы ваш код работал быстро даже на больших объемах данных.

← На главную