При разработке отчетов и механизмов расчета периодически возникает задача получить последовательность дат с заданным шагом (например, смещение на две недели назад от текущей даты до начала года). Так как язык запросов 1С не поддерживает циклы и рекурсивные выражения (Common Table Expressions / CTE), прямое циклическое вычисление внутри одного запроса невозможно.
Рассмотрим по шагам несколько эффективных способов решения этой задачи: генерацию последовательности дат с помощью таблицы цифр непосредственно в запросе, предварительную подготовку данных в коде 1С и отбор по календарю.
Проанализируем подход, при котором динамическая последовательность шагов формируется прямо в запросе. Для этого создается временная таблица с цифрами от 0 до 9, после чего выполняется её декартово произведение (внутреннее соединение по условию ПО ИСТИНА). Это позволяет сгенерировать ряды чисел (0, 1, 2, ..., N), которые затем умножаются на нужный интервал в днях (14 дней = 2 недели) и вычитаются из начальной даты с помощью функции ДОБАВИТЬКДАТЕ.
Рассмотрим пример запроса:
ВЫБРАТЬ
0 КАК Цифры
ПОМЕСТИТЬ ВТ_Цифры
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 5
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&ТекущаяДата, ДЕНЬ, -(ВТ_Цифры.Цифры + ВТ_Цифры1.Цифры * 10 + ВТ_Цифры2.Цифры * 100) * 14) КАК ДатаСИнтервалом2Недели
ИЗ
ВТ_Цифры КАК ВТ_Цифры
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Цифры КАК ВТ_Цифры1
ПО (ИСТИНА)
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Цифры КАК ВТ_Цифры2
ПО (ИСТИНА)
ГДЕ
ДОБАВИТЬКДАТЕ(&ТекущаяДата, ДЕНЬ, -(ВТ_Цифры.Цифры + ВТ_Цифры1.Цифры * 10 + ВТ_Цифры2.Цифры * 100) * 14) >= &ДатаНачала
УПОРЯДОЧИТЬ ПО
ДатаСИнтервалом2Недели УБЫВ
Разберем детально, как работает этот код:
ВТ_Цифры создаются 10 строк с числами от 0 до 9.ВТ_Цифры само с собой позволяет сформировать диапазон значений от 0 до 999.-(ВТ_Цифры.Цифры + ВТ_Цифры1.Цифры * 10 + ВТ_Цифры2.Цифры * 100) * 14 переводит индекс строки в смещение на нужную величину в днях.ДОБАВИТЬКДАТЕ вычитает полученное количество дней из параметра &ТекущаяДата.ГДЕ ограничивает выборку требуемым периодом (например, параметром &ДатаНачала).Выясним причину, почему в некоторых случаях предпочтительнее выносить расчет дат из запроса в код 1С. Генерация сложных математических цепочек в запросе может усложнять его чтение и отладку. Если список дат нужен для последующей передачи в условия запроса или временную таблицу, удобнее сформировать его обычным циклом в встроенном языке.
Посмотрим на пример реализации в коде:
КоличествоСекундВСутках = 86400;
ИнтервалДвеНедели = 14 * КоличествоСекундВСутках;
ТекДата = ТекущаяДатаСеанса();
НачалоПериода = НачалоГода(ТекДата);
ТаблицаДат = Новый ТаблицаЗначений;
ТаблицаДат.Колонки.Добавить("Период", Новый ОписаниеТипов("Дата"));
Пока ТекДата >= НачалоПериода Цикл
НоваяСтрока = ТаблицаДат.Добавить();
НоваяСтрока.Период = ТекДата;
ТекДата = ТекДата - ИнтервалДвеНедели;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Период КАК ДатаИнтервала
|ПОМЕСТИТЬ ВТ_Даты
|ИЗ
| &МассивДат КАК ТЗ";
Запрос.УстановитьПараметр("МассивДат", ТаблицаДат);
Этот подход гарантирует высокую скорость работы и наглядность алгоритма, так как подготовка данных происходит в оперативной памяти до передачи в СУБД.
Если в вашей конфигурации присутствует заполненный производственный календарь или специальный справочник дат, можно выбрать нужные периоды из него. Для этого рассчитывается разница в днях между датой календаря и целевой датой, после чего отбираются записи, кратные 14 дням.
Рассмотрим пример запроса по существующему календарю:
ВЫБРАТЬ
Календарь.Дата КАК ДатаСИнтервалом
ИЗ
РегистрСведений.ПроизводственныйКалендарь КАК Календарь
ГДЕ
Календарь.Дата <= &ТекущаяДата
И Календарь.Дата >= &ДатаНачала
И РАЗНОСТЬДАТ(Календарь.Дата, &ТекущаяДата, ДЕНЬ) - ВЫРАЗИТЬ(РАЗНОСТЬДАТ(Календарь.Дата, &ТекущаяДата, ДЕНЬ) / 14 - 0.5 КАК ЧИСЛО(10, 0)) * 14 = 0
УПОРЯДОЧИТЬ ПО
ДатаСИнтервалом УБЫВ
Важный момент: при использовании вычисляемых функций (таких как РАЗНОСТЬДАТ) в условиях фильтрации ГДЕ, СУБД не может применить индексы по полю даты, что может снизить производительность при выборке из больших таблиц. Поэтому для частых и критичных к скорости вычислений рекомендуется применять Способ 2 или механизмы схемы компоновки данных (СКД) с настройкой Дополнения периодов.