При построении отчетов и выборок по периодическим регистрам сведений часто требуется получить динамику показателей (например, курсов валют или цен) с регулярным шагом — по дням, неделям или месяцам. Для этого разработчики нередко применяют конструкцию языка запросов ПЕРИОДАМИ в секции ИТОГИ. Однако при обработке результатов такого запроса во встроенном языке можно столкнуться с ошибками выборки и пропаданием данных в детальных строках. Разберем по шагам, почему возникают подобные проблемы и как правильно организовать получение и обход данных.
Проанализируем распространенную ошибку. При попытке перечислить несколько группировок через запятую в методе Выбрать():
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Валюта,Период", "ВСЕ");
Платформа 1С:Предприятие генерирует исключение. Метод Выбрать() при обходе по группировкам не принимает список измерений через запятую в одной строке. Он рассчитан на строгую иерархическую структуру дерева результатов запроса. Каждая иерархическая группировка, указанная в секции ИТОГИ ПО, образует отдельный уровень вложенности.
Если в запросе задано ИТОГИ ПО Валюта, Период ПЕРИОДАМИ(...), то в результате запроса создаются следующие уровни:
Валюта;Период для каждой валюты;Если пропустить верхний уровень и попытаться сразу выбрать только Период, то контекст верхнего измерения (в данном случае конкретная валюта и её реквизиты) будет потерян.
Рассмотрим, как корректно обойти результат запроса, содержащий несколько уровней итогов, с помощью последовательных вложенных выборок.
// 1. Выбираем верхний уровень группировки - Валюта
ВыборкаВалюта = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Валюта");
Пока ВыборкаВалюта.Следующий() Цикл
// Выводим заголовок по валюте при необходимости
// ОбластьШапка.Параметры.Заполнить(ВыборкаВалюта);
// ТабДок.Вывести(ОбластьШапка);
// 2. Из текущей валюты выбираем подчиненную группировку - Период
ВыборкаПериод = ВыборкаВалюта.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Период", "ВСЕ");
Пока ВыборкаПериод.Следующий() Цикл
// 3. Выбираем детальные записи внутри конкретного периода
ВыборкаДетали = ВыборкаПериод.Выбрать();
Пока ВыборкаДетали.Следующий() Цикл
ОбластьДетальныхЗаписей.Параметры.Заполнить(ВыборкаДетали);
ТабДок.Вывести(ОбластьДетальныхЗаписей);
КонецЦикла;
КонецЦикла;
КонецЦикла;
Параметр "ВСЕ" в методе выборки периода указывает платформе, что необходимо вернуть все точки временной сетки (включая интервалы, на которые не было реальных движений в регистре).
Важно понимать поведение конструкции ПЕРИОДАМИ:
NULL.Если стоит задача получить полноценный срез периодического регистра сведений на каждый день или неделю с сохранением последнего актуального значения, рассмотрим универсальный подход с генерацией таблицы дат в запросе и соединением по условию неравенства.
Разберем алгоритм построения запроса:
Регистр.Период <= ТаблицаДат.Период;Посмотрим на пример текста запроса:
ВЫБРАТЬ
Календарь.Период КАК КонтрольнаяДата,
Валюты.Ссылка КАК Валюта,
МАКСИМУМ(КурсыВалют.Период) КАК ПериодЗаписи
ПОМЕСТИТЬ ВТ_СрезДат
ИЗ
ВТ_КалендарныеДаты КАК Календарь
КРЕСТНОЕ СОЕДИНЕНИЕ Справочник.Валюты КАК Валюты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
ПО (КурсыВалют.Валюта = Валюты.Ссылка)
И (КурсыВалют.Период <= Календарь.Период)
СГРУППИРОВАТЬ ПО
Календарь.Период,
Валюты.Ссылка
ИНДЕКСИРОВАТЬ ПО
Валюта,
ПериодЗаписи
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Срез.КонтрольнаяДата КАК Период,
Срез.Валюта КАК Валюта,
ЕСТЬNULL(Курсы.Курс, 0) КАК Курс
ИЗ
ВТ_СрезДат КАК Срез
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Курсы
ПО (Срез.Валюта = Курсы.Валюта)
И (Срез.ПериодЗаписи = Курсы.Период)
УПОРЯДОЧИТЬ ПО
Валюта,
Период
Такой подход гарантирует, что в результирующей выборке на каждую дату диапазона будут получены корректные значения курса без пробелов, а результат можно будет обойти стандартным линейным циклом без усложнения иерархии итогов.