Как в запросе 1С получить таблицу диапазонов дат?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С для быстрого анализа данных часто возникает задача преобразования простого списка событий, привязанных к дате, в таблицу периодов. Например, когда у нас есть история изменения цен или состояний объектов, и нам нужно понять, в каком интервале (от и до) действовало каждое конкретное значение — для этого подойдёт отчет для определения цены на дату документа. Проанализируем, как можно эффективно решить эту задачу различными способами, начиная от классических запросов и заканчивая современными инструментами платформы.

Способ 1. Левое соединение таблицы самой с собой (Классический метод)

Разберем самый распространенный способ, который работает на любой версии платформы 8.x — для этого подойдёт инструмент регистрации истории изменений объектов. Идея заключается в том, чтобы для каждой строки из исходного набора данных найти все строки, дата которых больше текущей, и среди них выбрать самую раннюю. Это и будет «дата окончания» или «дата начала следующего периода».

Предположим, наши данные находятся во временной таблице ВТ_Данные (подготовить их можно через Консоль запросов УФ от Dr.Zombi (удобно через расширенная консоль запросов для управляемых форм)). Рассмотрим текст запроса, который реализует эту логику:


ВЫБРАТЬ
    ВТ.Ссылка КАК Ссылка,
    ВТ.Дата КАК ДатаНачала,
    МИНИМУМ(ВТ_След.Дата) КАК ДатаОкончания
ИЗ
    ВТ_Данные КАК ВТ
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные КАК ВТ_След
        ПО ВТ.Дата < ВТ_След.Дата
СГРУППИРОВАТЬ ПО
    ВТ.Ссылка,
    ВТ.Дата

Проанализируем по шагам, что происходит в этом запросе:

  1. ЛЕВОЕ СОЕДИНЕНИЕ: Мы присоединяем ту же самую таблицу. Условие ВТ.Дата < ВТ_След.Дата заставляет систему для каждой записи найти все последующие записи.
  2. МИНИМУМ: Поскольку последующих записей может быть много, нам нужна только та, что идет сразу за текущей. Функция МИНИМУМ по полю даты из присоединенной таблицы идеально решает эту задачу.
  3. СГРУППИРОВАТЬ ПО: Это обязательное условие при использовании агрегатных функций (таких как МИНИМУМ). Мы группируем результат по идентификатору (ссылке) и исходной дате.

Важный нюанс: Для последней записи в таблице не найдется даты, которая была бы больше её собственной. В этом случае поле ДатаОкончания примет значение NULL. Чтобы «закрыть» последний период какой-то конкретной датой (например, концом времен или текущей датой), используйте функцию ЕСТЬNULL (это актуально и при использовании способа расчета остатков на каждый день).

Способ 2. Оконные функции (Для платформы 8.3.20 и выше)

Если вы работаете на современной версии платформы, использование соединений «самой с собой» является неоптимальным. Соединения по условию «больше/меньше» создают серьезную нагрузку на СУБД при больших объемах данных. Выясним, как сделать это проще с помощью оконных функций.

В языке запросов 1С появилась замечательная функция СЛЕДУЮЩЕЕ (аналог SQL-функции LEAD). С её помощью запрос становится линейным и выполняется значительно быстрее.


ВЫБРАТЬ
    ВТ.Ссылка,
    ВТ.Дата КАК ДатаНачала,
    СЛЕДУЮЩЕЕ(ВТ.Дата) НАД(УПОРЯДОЧИТЬ ПО ВТ.Дата) КАК ДатаОкончания
ИЗ
    ВТ_Данные КАК ВТ

Посмотрим на преимущества этого подхода:

Способ 3. Использование Системы компоновки данных (СКД)

Иногда задачу нужно решить не в «чистом» коде, а в отчете. В СКД есть мощный механизм работы с контекстом записей через вычисляемые выражения. Если мы хотим получить дату следующей строки, мы можем создать вычисляемое поле.

Разберем алгоритм настройки:

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

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

Важные рекомендации по оптимизации

Независимо от выбранного способа, следует помнить о нескольких критических моментах, которые могут повлиять на корректность и скорость работы:

1. Индексация: Если вы используете Способ №1 (соединение через временные таблицы), обязательно добавьте Индексировать по полю Дата (и по полям группировки, если они есть). Без индекса на больших таблицах запрос может выполняться недопустимо долго.

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

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


ВЫБРАТЬ
    ВТ.Ссылка,
    ВТ.Дата КАК ДатаНачала,
    ЕСТЬNULL(СЛЕДУЮЩЕЕ(ВТ.Дата) НАД(УПОРЯДОЧИТЬ ПО ВТ.Дата), &ДатаКонца) КАК ДатаОкончания
ИЗ
    ВТ_Данные КАК ВТ

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

← На главную