При написании динамических отчетов и обработок часто возникает задача группировать или округлять даты с различной периодичностью (по дням, месяцам, кварталам или годам) в зависимости от выбора пользователя. Разработчики нередко пытаются передать периодичность напрямую в функцию языка запросов:
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(&Дата, &ТипПериода) КАК Период
При выполнении такого кода платформа 1С возвращает ошибку о неверных параметрах. Выясним причину такого поведения и рассмотрим проверенные способы реализации динамической периодичности.
Проанализируем архитектурную особенность языка запросов 1С. Второй параметр функций работы с датами (НАЧАЛОПЕРИОДА, КОНЕЦПЕРИОДА, ДОБАВИТЬКДАТЕ, РАЗНОСТЬДАТ) является фиксированным ключевым словом (лексемой), а не выражением или скалярным значением. На этапе синтаксического анализа и компиляции платформа 1С транслирует это ключевое слово в платформо-зависимые инструкции конкретной СУБД (например, специфические вызовы функций работы с датами в MS SQL Server или PostgreSQL).
Так как второй параметр не является значением, подставить туда динамический параметр через символ & невозможно. В платформе 1С определен строгий перечень допустимых ключевых слов для типа периода:
Разберем основные методы, позволяющие обойти данное ограничение на практике.
Посмотрим на вариант, когда текст запроса остается статическим, а нужный тип периода определяется логикой внутри самого запроса с помощью оператора ВЫБОР. Этот подход удобен тем, что текст запроса можно редактировать в стандартном конструкторе запросов без строковых манипуляций.
ВЫБРАТЬ
ВЫБОР &ТипПериода
КОГДА "День" ТОГДА
НАЧАЛОПЕРИОДА(Таблица.Дата, ДЕНЬ)
КОГДА "Неделя" ТОГДА
НАЧАЛОПЕРИОДА(Таблица.Дата, НЕДЕЛЯ)
КОГДА "Месяц" ТОГДА
НАЧАЛОПЕРИОДА(Таблица.Дата, МЕСЯЦ)
КОГДА "Квартал" ТОГДА
НАЧАЛОПЕРИОДА(Таблица.Дата, КВАРТАЛ)
КОГДА "Год" ТОГДА
НАЧАЛОПЕРИОДА(Таблица.Дата, ГОД)
ИНАЧЕ
Таблица.Дата
КОНЕЦ КАК Период
ИЗ
РегистрНакопления.Продажи КАК Таблица
При передаче параметра в коде встроенного языка достаточно передать строку с наименованием варианта:
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("ТипПериода", "Месяц");
Результат = Запрос.Выполнить();
Важный нюанс производительности: конструкция ВЫБОР КОГДА транслируется на сторону СУБД в оператор CASE. Если полученное поле используется в секциях соединения таблиц ПО или в группировке СГРУППИРОВАТЬ ПО, СУБД не сможет использовать стандартные индексы по исходному полю даты. Это может вызвать серьезное замедление при обработке больших массивов данных. Конструкция ВЫБОР идеальна, если периодичность может различаться для разных строк таблицы или если объем выборки небольшой.
Если тип периода задается пользователем один раз для всего отчета или алгоритма, наиболее эффективным решением с точки зрения СУБД является динамическая модификация текста запроса до его передачи на выполнение. В этом случае в результирующем SQL-запросе будет присутствовать чистый расчет без лишних ветвлений.
Рассмотрим реализацию через функцию СтрШаблон или метод СтрЗаменить. Обязательно выполним валидацию входящего значения по списку допустимых слов, чтобы исключить синтаксические ошибки формирования запроса:
// Определим допустимые типы периодов
ДопустимыеПериоды = Новый Соответствие;
ДопустимыеПериоды.Вставить("ДЕНЬ", "ДЕНЬ");
ДопустимыеПериоды.Вставить("НЕДЕЛЯ", "НЕДЕЛЯ");
ДопустимыеПериоды.Вставить("МЕСЯЦ", "МЕСЯЦ");
ДопустимыеПериоды.Вставить("КВАРТАЛ", "КВАРТАЛ");
ДопустимыеПериоды.Вставить("ГОД", "ГОД");
Периодичность = ВРег(СокрЛП(ВыбраннаяПериодичность));
Если ДопустимыеПериоды[Периодичность] = Неопределено Тогда
Периодичность = "ДЕНЬ"; // Значение по умолчанию
КонецЕсли;
ШаблонЗапроса =
"ВЫБРАТЬ
| НАЧАЛОПЕРИОДА(Продажи.Период, %1) КАК Период,
| СУММА(Продажи.Сумма) КАК Сумма
|ИЗ
| РегистрНакопления.Продажи КАК Продажи
|СГРУППИРОВАТЬ ПО
| НАЧАЛОПЕРИОДА(Продажи.Период, %1)";
Запрос = Новый Запрос;
Запрос.Текст = СтрШаблон(ШаблонЗапроса, Периодичность);
Результат = Запрос.Выполнить();
В более старых версиях платформы, где функция СтрШаблон недоступна, аналогичным образом применим метод СтрЗаменить, заменив заранее подготовленный маркер (например, &Периодичность) на проверенную строку.
Когда требуется собрать сложный динамический запрос с сохранением синтаксической строгости без ручной конкатенации строк, разберем работу с объектом СхемаЗапроса. Этот объект представляет текст запроса в виде объектной модели и дерева выражений.
Используя СхемаЗапроса, мы можем программно найти нужное поле в коллекции колонок или условий и подставить в выражение требуемую текстовую конструкцию, после чего получить готовый текст запроса через вызов Схема.ПолучитьТекстЗапроса().
Если задача решается в рамках Системы компоновки данных, модифицировать текст запроса вручную чаще всего вообще не требуется:
ПериодСекунда, ПериодДень, ПериодНеделя, ПериодМесяц, ПериодКвартал, ПериодГод) при включенном флаге автозаполнения или явном указании ролей полей в схеме.Таким образом, для простых отчетов на СКД лучше доверить разделение по периодам самой компоновке данных, а для классических запросов во встроенном языке — использовать безопасную текстовую подстановку через СтрШаблон или конструкцию ВЫБОР КОГДА.