Работа с временными интервалами является одной из самых частых задач при разработке на платформе 1С:Предприятие 8. Часто аналитику необходимо не просто получить данные за период, а сформировать сплошной список дат, даже если в эти дни не было хозяйственных операций. Например, для построения графика продаж по дням или расчета остатков на каждый день месяца.

В стандартном языке запросов 1С нет прямой функции, которая бы просто «сгенерировала» календарь по клику. Однако существует несколько проверенных методов решения этой задачи: от использования временных таблиц до хитростей с виртуальными таблицами накопительных регистров. Выбор конкретного способа зависит от версии платформы и требований к производительности.

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

Зачем нужен сплошной список дат в отчетах

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

Список дат выступает в роли каркаса или скелета для вашего отчета. Объединяя этот список с реальными данными через внешнее соединение, разработчик гарантирует, что в итоговой таблице будут представлены все дни выбранного периода. Это критически важно для сравнительного анализа и расчета скользящих средних.

Представьте, что вы строите отчет по продажам за неделю. Если в субботу и воскресенье продаж не было, стандартный запрос вернет только 5 строк. Но менеджеру нужно видеть 7 строк с нулями, чтобы понимать, что выходные были, а не то, что данные потерялись. Использование временной таблицы с датами решает эту проблему элегантно и эффективно.

⚠️ Внимание: При использовании внешних соединений помните, что порядок строк в результирующей выборке может измениться. Всегда явно указывайте поле даты в секции УПОРЯДОЧИТЬ ПО, чтобы сохранить хронологическую последовательность.

📊 Какой метод формирования дат вы используете чаще?
Цикл в коде + Временная таблица
Виртуальная таблица ПериодыРегистрации
Таблица значений на клиенте
Готовые обработки из библиотеки

Классический метод: цикл и временная таблица

Самый универсальный и понятный способ — это формирование списка дат в коде программы с последующей загрузкой во временную таблицу. Этот метод работает на любых версиях платформы и не зависит от наличия конкретных регистров в конфигурации.

Алгоритм прост: вы создаете объект ТаблицаЗначений, добавляете в него колонку типа Дата, а затем в цикле заполняете ее, прибавляя один день к начальной дате until достижения конечной даты. После заполнения таблица загружается во временную таблицу запроса.

Такой подход дает полный контроль над процессом. Вы можете легко модифицировать цикл, чтобы добавлять только рабочие дни, исключать праздники или формировать список с шагом в неделю или месяц. Гибкость этого метода делает его стандартом де-факто для сложных отчетов.

ПериодНачала = НачалоДня(Период.ДатаНачала);

ПериодОкончания = КонецДня(Период.ДатаОкончания);

ТаблицаДат = Новый ТаблицаЗначений;

ТаблицаДат.Колонки.Добавить("Дата", ТипОписанияТипов.ПолучитьТип("Дата"));

ТекущаяДата = ПериодНачала;

Пока ТекущаяДата <= ПериодОкончания Цикл

НоваяСтрока = ТаблицаДат.Добавить();

НоваяСтрока.Дата = ТекущаяДата;

ТекущаяДата = ТекущаяДата + 1;

КонецЦикла;

Запрос.Текст = "

|ВЫБРАТЬ

| ТаблицаДат.Дата КАК ДатаОтчета

|ИЗ

| &ТаблицаДат КАК ТаблицаДат";

Запрос.УстановитьПараметр("ТаблицаДат", ТаблицаДат);

☑️ Алгоритм создания временной таблицы

Выполнено: 0 / 5

Использование виртуальной таблицы ПериодыРегистрации

В конфигурациях на базе БСП или типовых решениях часто используется хитрый прием с регистром сведений «ПериодыРегистрации». Этот регистр технически содержит записи за каждый день существования базы, что позволяет использовать его как источник сплошного календаря.

Обращение к этому регистру через виртуальную таблицу позволяет получить список дат без написания циклов в коде. Запрос становится чище и выполняется быстрее, так как вся работа по генерации перекладывается на сервер баз данных. Это особенно актуально при больших объемах данных.

Однако у этого метода есть ограничение: он работает только если в конфигурации действительно есть такой регистр и в нем есть записи за нужный период. В пустой базе или в самописных конфигурациях этот способ может не сработать без предварительной подготовки данных.

Запрос.Текст = "

|ВЫБРАТЬ

| ПериодыРегистрацииСрезПоследних.Период КАК ДатаОтчета

|ИЗ

| РегистрСведений.ПериодыРегистрации.СрезПоследних(

| &ДатаОкончания,

| ) КАК ПериодыРегистрацииСрезПоследних

|ГДЕ

| ПериодыРегистрацииСрезПоследних.Период >= &ДатаНачала";

Нюансы работы с ПериодамиРегистрации

Виртуальная таблица СрезПоследних возвращает только одну запись на измерение. Поскольку у регистра ПериодыРегистрации нет измерений, он вернет одну запись — последнюю дату. Чтобы получить список, нужно обращаться к физической таблице или использовать хитрости с группировкой, но проще использовать таблицу значений.

Генерация дат через систему типов и таблицу значений

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

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

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

Метод Производительность Читаемость кода Зависимость от конфигурации
Цикл + ТаблицаЗначений Средняя Высокая Нет
ПериодыРегистрации Высокая Средняя Высокая
Генерация в запросе Высокая Низкая Средняя
💡

Для ускорения работы с большими периодами (более года) рассмотрите возможность кэширования таблицы дат в общей временной таблице с длительным временем жизни, если отчет формируется часто.

Обработка рабочих и праздничных дней

Часто бизнесу требуется анализ не по календарным дням, а по рабочим. В этом случае простой инкремент даты на единицу не подойдет. Необходимо интегрировать логику проверки производственного календаря прямо в процесс формирования списка.

В платформе 1С существует объект ПроизводственныйКалендарь, который позволяет определить, является ли дата рабочей. Вы можете использовать его в цикле заполнения таблицы значений. Если день является выходным, вы либо пропускаете его, либо помечаете специальным флагом.

Для сложных случаев, когда график работы предприятия отличается от государственного (например, сменный график 2/2), лучше хранить календарь в собственном регистре сведений. Это позволит делать выборки непосредственно средствами запроса, присоединяя таблицу рабочих дней.

⚠️ Внимание: Производственный календарь может меняться законодательно (переносы выходных). Жестко прописанные условия в коде могут устареть. Рекомендуется сверять актуальные правила переноса в официальных источниках или использовать обновляемые справочники в базе 1С.

Оптимизация и производительность при больших периодах

При формировании списка дат за несколько лет количество строк во временной таблице может достигать тысяч и десятков тысяч. Хотя для 1С это не критично, неоптимальное соединение такой таблицы с основными данными может привести к тормозам отчета.

Индексация временной таблицы — ключевой момент оптимизации. При создании таблицы значений убедитесь, что поле даты участвует в условиях соединения. В некоторых случаях полезно добавить вспомогательные колонки, например, «Год» или «Месяц», если группировка идет по ним.

Также стоит избегать лишних преобразований типов внутри запроса. Если вы передали таблицу с типом Дата, не пытайтесь привести её к строке или числу в условии ГДЕ. Пусть СУБД использует стандартные механизмы сравнения дат для максимальной скорости.

💡

Использование временной таблицы с предварительно заполненными данными — наиболее стабильный и предсказуемый метод, который гарантирует одинаковое поведение на файловых и клиент-серверных базах.

Частые ошибки и способы их устранения

Одна из самых распространенных ошибок — некорректное определение границ периода. Начинающие разработчики часто забывают про функцию КонецДня или НачалоДня, из-за чего последний день периода выпадает из выборки из-за разницы во времени.

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

Не забывайте про типизацию параметров. Если в запросе ожидается Дата, а передается Строка, механизм запроса может не сработать или выполнить неявное преобразование, которое замедлит выполнение. Явное приведение типов повышает надежность кода.

Как получить список первых чисел каждого месяца?

Для этого нужно модифицировать цикл заполнения. Вместо прибавления одного дня, используйте функцию ДобавитьМесяц. Начните с даты НачалоМесяца(ДатаНачала) и в цикле добавляйте по одному месяцу, пока дата не превысит ДатаОкончания.

Можно ли сгенерировать даты прямо в СКД без кода?

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

Почему запрос с временной таблицей работает медленнее на SQL Server?

Это может быть связано с отсутствием статистики по временной таблице или блокировками. Попробуйте добавить опцию НЕ ПЕРЕСЧИТЫВАТЬ или убедитесь, что соединяемые поля имеют совместимые типы данных для использования индексов.

Как исключить будущие даты из списка?

Добавьте условие в цикл формирования: Если ТекущаяДата > ТекущаяДата() Тогда Прервать; КонецЕсли;. Либо отфильтруйте результат запросом, добавив условие ГДЕ ДатаОтчета <= &ТекущаяДата.