Почему двоятся строки и искажаются суммы при использовании ИТОГИ ПО в запросе 1С?

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

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

Причина 1. Выгрузка запроса с итогами в плоскую таблицу значений

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

В такой таблице одна и та же реализация визуально встречается несколько раз:

  1. На первой строке заполнен только менеджер и рассчитана общая сумма по нему (остальные поля содержат NULL или пустые значения).
  2. На второй строке заполнены менеджер и контрагент, а также подитог по этой паре.
  3. На последующих строках выводятся детальные данные документов.

Если требуется получить визуальное дерево или правильную плоскую структуру, используем следующие подходы:

  1. Выгрузка в дерево значений с сохранением иерархии: Дерево = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
  2. Использование конструкции СГРУППИРОВАТЬ ПО вместо ИТОГИ ПО, если промежуточные расчетные уровни в запросе не требуются, а нужна лишь агрегация данных на одном уровне.

Причина 2. Соединение «один ко многим» и умножение сумм в итогах

Выясним причину, из-за которой агрегатные функции СУММА() в секции ИТОГИ возвращают завышенные результаты. В отчете присутствуют поля документа реализации и документа оплаты. Если к одной накладной проведено несколько платежей (или частичных оплат), левое или внутреннее соединение размножает строку реализации по количеству строк оплат.

В детальных строках плоской таблицы сумма документа может выглядеть привычно, но директива ИТОГИ СУММА(РеализацияНаСумму) ПО ТорговыйАгент выполняет простое арифметическое суммирование по всем строкам выборки. Если документ оплаты разбился на 4 строки, сумма реализации суммируется 4 раза.

Чтобы избежать искажения сумм, разделим сбор данных на предварительные этапы во временных таблицах:

  1. В первой временной таблице рассчитываем остатки и суммы документов реализации (строго по одной строке на документ).
  2. Во второй временной таблице агрегируем оплаты по документам реализации.
  3. В финальном запросе соединяем подготовленные таблицы по ключу документа по принципу «один к одному».

Причина 3. Ошибки обхода выборки и появление пустых значений

Рассмотрим, почему при обходе выборки возникают пустые значения. В строках группировок верхнего уровня детальные поля не определены и равны NULL. Доступны только само группировочное поле и поля, для которых в секции ИТОГИ явно указаны агрегатные функции (СУММА, МИНИМУМ, МАКСИМУМ).

Кроме того, обход выборки с несколькими уровнями группировок должен строиться каскадно: каждый следующий уровень необходимо запрашивать из выборки предыдущего уровня, а не из исходного объекта РезультатЗапроса.

Разберем правильный алгоритм многоуровневого обхода выборки:


// Получаем выборку верхнего уровня по торговому агенту
ВыборкаАгент = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Менеджер");

Пока ВыборкаАгент.Следующий() Цикл
    // Выводим строку итогов по агенту
    // Доступны поле ВыборкаАгент.Менеджер и агрегаты СУММА(...)
    
    // Получаем выборку второго уровня из текущей строки агента
    ВыборкаКонтрагент = ВыборкаАгент.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Контрагент");
    
    Пока ВыборкаКонтрагент.Следующий() Цикл
        // Выводим строку итогов по контрагенту
        
        // Получаем детальные записи по документам
        ВыборкаДетали = ВыборкаКонтрагент.Выбрать(ОбходРезультатаЗапроса.Прямой);
        
        Пока ВыборкаДетали.Следующий() Цикл
            // Выводим детальные поля: документ реализации, дата, оплата
        КонецЦикла;
        
    КонецЦикла;
КонецЦикла;

При проверке типа текущей записи в смешанных выборках анализируем системное свойство Выборка.ТипЗаписи с помощью системного перечисления ТипЗаписиЗапроса (значения ИтогПоГруппировке, ДетальнаяЗапись, ОбщийИтог).

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

Для вывода иерархических отчетов с промежуточными итогами по агентам и контрагентам оптимально применять СКД. В схеме компоновки данных достаточно передать плоский запрос с детальными полями без блока ИТОГИ. Система компоновки данных автоматически рассчитывает ресурсы на нужных уровнях группировок, исключает дублирование строк при выводе в табличный документ и корректно сворачивает показатели задолженности.

← На главную