Как правильно обойти итоги запроса с конструкцией ПЕРИОДАМИ по нескольким группировкам в 1С?

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

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

Причины ошибки «Различное количество измерений в списке измерений и списке источников...»

Проанализируем распространенную ошибку. При попытке перечислить несколько группировок через запятую в методе Выбрать():


ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Валюта,Период", "ВСЕ");

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

Если в запросе задано ИТОГИ ПО Валюта, Период ПЕРИОДАМИ(...), то в результате запроса создаются следующие уровни:

  1. Верхний уровень группировки по полю Валюта;
  2. Вложенный уровень группировки по полю Период для каждой валюты;
  3. Уровень детальных записей внутри каждого периода.

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

Решение 1. Правильный иерархический обход группировок во встроенном языке

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


// 1. Выбираем верхний уровень группировки - Валюта
ВыборкаВалюта = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Валюта");

Пока ВыборкаВалюта.Следующий() Цикл
    // Выводим заголовок по валюте при необходимости
    // ОбластьШапка.Параметры.Заполнить(ВыборкаВалюта);
    // ТабДок.Вывести(ОбластьШапка);
    
    // 2. Из текущей валюты выбираем подчиненную группировку - Период
    ВыборкаПериод = ВыборкаВалюта.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Период", "ВСЕ");
    
    Пока ВыборкаПериод.Следующий() Цикл
        
        // 3. Выбираем детальные записи внутри конкретного периода
        ВыборкаДетали = ВыборкаПериод.Выбрать();
        
        Пока ВыборкаДетали.Следующий() Цикл
            ОбластьДетальныхЗаписей.Параметры.Заполнить(ВыборкаДетали);
            ТабДок.Вывести(ОбластьДетальныхЗаписей);
        КонецЦикла;
        
    КонецЦикла;
КонецЦикла;

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

Особенности и ограничения конструкции «ПЕРИОДАМИ»

Важно понимать поведение конструкции ПЕРИОДАМИ:

  1. Данные формируются только на уровне итогов: Конструкция не создает детальных записей в таблице. Если в указанный интервал (например, в определенную неделю) курс валюты не менялся, на детальном уровне записей не будет вообще, а на уровне итогов значения полей без агрегатных функций вернут NULL.
  2. Отсутствие «протягивания» значений: Данная конструкция не находит автоматически последнее актуальное значение на пустые даты. Она лишь генерирует календарную шкалу периодов.

Решение 2. Классический «Срез последних на каждую дату» через временные таблицы

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

Разберем алгоритм построения запроса:

  1. Формируем временную таблицу с требуемым списком контрольных дат (из производственного календаря или переданной таблицы значений);
  2. Выполняем соединение таблицы дат со списком измерений и регистром сведений по условию Регистр.Период <= ТаблицаДат.Период;
  3. Группировкой по дате и измерению находим максимальный период фактической записи;
  4. Соединяем полученный срез дат с физической таблицей регистра для получения ресурсов (курса, кратности).

Посмотрим на пример текста запроса:


ВЫБРАТЬ
    Календарь.Период КАК КонтрольнаяДата,
    Валюты.Ссылка КАК Валюта,
    МАКСИМУМ(КурсыВалют.Период) КАК ПериодЗаписи
ПОМЕСТИТЬ ВТ_СрезДат
ИЗ
    ВТ_КалендарныеДаты КАК Календарь
    КРЕСТНОЕ СОЕДИНЕНИЕ Справочник.Валюты КАК Валюты
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
        ПО (КурсыВалют.Валюта = Валюты.Ссылка)
        И (КурсыВалют.Период <= Календарь.Период)
СГРУППИРОВАТЬ ПО
    Календарь.Период,
    Валюты.Ссылка
ИНДЕКСИРОВАТЬ ПО
    Валюта,
    ПериодЗаписи
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Срез.КонтрольнаяДата КАК Период,
    Срез.Валюта КАК Валюта,
    ЕСТЬNULL(Курсы.Курс, 0) КАК Курс
ИЗ
    ВТ_СрезДат КАК Срез
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Курсы
        ПО (Срез.Валюта = Курсы.Валюта)
        И (Срез.ПериодЗаписи = Курсы.Период)
УПОРЯДОЧИТЬ ПО
    Валюта,
    Период

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

← На главную