В процессе разработки отчетов на базе Системы компоновки данных (СКД) часто возникает задача упростить интерфейс для пользователя. Например, вместо выбора точной даты с временем, пользователю удобнее ввести четырехзначное число (год), а отчет должен автоматически сформироваться за этот период. Однако на уровне логики запроса нам требуется именно тип Дата для сравнения с полями базы данных. Разберем подробно, как реализовать такое преобразование разными способами.
Рассмотрим ситуацию, когда у нас уже есть параметр &Год (тип Число), и нам нужно получить из него дату прямо в теле запроса. Основная сложность заключается в том, что стандартная функция ДАТАВРЕМЯ в языке запросов 1С принимает в качестве аргументов только константы. Мы не можем написать ДАТАВРЕМЯ(&Год, 1, 1) — это вызовет ошибку синтаксиса.
Для решения этой задачи проанализируем функцию ДОБАВИТЬКДАТЕ. Она позволяет прибавлять к уже существующей дате определенное количество временных интервалов (год, месяц, день). Мы можем взять «нулевую» дату платформы (01.01.0001) и прибавить к ней нужное количество лет.
Разберем по шагам логику вычисления:
ДАТАВРЕМЯ(1, 1, 1).Пример реализации в запросе:
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), ГОД, &Год - 1) КАК НачалоГода,
ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), ГОД, &Год), ДЕНЬ, -1) КАК КонецГода
Этот метод универсален и работает непосредственно на стороне СУБД, что полезно, если вы не хотите полагаться на механизмы предварительной обработки параметров СКД.
Система компоновки данных предоставляет более элегантный механизм — закладку «Параметры». Здесь мы можем разделить параметр, который видит пользователь, и параметр, который используется в запросе.
Рассмотрим алгоритм настройки:
ГодЧислом (тип Число, длина 4). Именно его мы выводим в пользовательские настройки.ПериодОтчета (тип Дата). В колонке «Ограничение доступности» ставим галочку, чтобы пользователь его не видел.ПериодОтчета пишем код на языке выражений СКД.В языке выражений СКД, в отличие от языка запросов, доступна функция Дата(), аналогичная встроенному языку 1С. Пропишем следующее выражение:
Дата(&ГодЧислом, 1, 1)
Теперь в основном запросе вы можете смело использовать ГДЕ ПолеДата >= &ПериодОтчета. Платформа сама вычислит дату перед выполнением запроса.
Иногда программисты пытаются решить проблему сложным кодом там, где достаточно настройки интерфейса. Если ваша цель — чтобы пользователь вводил только год, можно оставить параметр типа Дата, но изменить способ его отображения.
Выясним, как это сделать:
Дата.ДФ=yyyy.В этом случае пользователь увидит в поле ввода только четыре цифры года. При выборе значения 1С автоматически подставит дату на начало этого года (01.01.ГГГГ 00:00:00). Это избавляет нас от необходимости писать какой-либо код для преобразования типов.
Если логика отчета требует сложной предварительной подготовки (например, год должен преобразовываться в финансовый период, зависящий от настроек учетной политики), лучше всего использовать процедуру ПриКомпоновкеРезультата в модуле объекта отчета.
Посмотрим на пример кода, который перехватывает управление:
Процедура ПриКомпоновкеРезультата(ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получаем настройки пользователя
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
// Ищем наш числовой параметр
ПараметрГод = Настройки.ПараметрыДанных.Элементы.Найти("Год");
Если ПараметрГод <> Неопределено Тогда
ГодЗначение = ПараметрГод.Значение;
// Преобразуем число в дату
ДатаНачала = Дата(ГодЗначение, 1, 1);
// Устанавливаем скрытый параметр для запроса
Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("НачалоПериода", ДатаНачала);
КонецЕсли;
// Выполняем компоновку
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Проанализируем ситуацию, затронутую в обсуждении: где эффективнее выполнять условия — в виртуальных таблицах или через ГДЕ? При использовании преобразований типов в запросе важно помнить о SARGability (способности СУБД использовать индексы).
Важные правила:
ГОД(Таблица.Дата) = &ВыбранныйГод заставит SQL-сервер сканировать всю таблицу (Index Scan или Table Scan), так как он не может использовать индекс по полю Дата.&Год к границам дат (Начало и Конец года) заранее. Выражение Таблица.Дата МЕЖДУ &НачалоГода И &КонецГода отработает мгновенно, используя индекс.Именно поэтому Способ 2 (Выражение в параметрах СКД) является наиболее предпочтительным: он сохраняет «чистоту» и производительность SQL-запроса, обеспечивая при этом удобство ввода для пользователя.
Если ваше решение подразумевает сложную логику, которую трудно описать одной строкой, вспомните о возможности вызова функций общих модулей в выражениях СКД. Например:
МойОбщийМодуль.ПолучитьНачалоПериода(&ГодЧислом)
Это позволит вам централизованно управлять логикой преобразования типов во всех отчетах системы. Помните, что для этого общий модуль должен иметь флаги Сервер, Внешнее соединение и Вызов сервера.