В практике разработки на платформе 1С для быстрого анализа данных часто возникает задача преобразования простого списка событий, привязанных к дате, в таблицу периодов. Например, когда у нас есть история изменения цен или состояний объектов, и нам нужно понять, в каком интервале (от и до) действовало каждое конкретное значение — для этого подойдёт отчет для определения цены на дату документа. Проанализируем, как можно эффективно решить эту задачу различными способами, начиная от классических запросов и заканчивая современными инструментами платформы.
Разберем самый распространенный способ, который работает на любой версии платформы 8.x — для этого подойдёт инструмент регистрации истории изменений объектов. Идея заключается в том, чтобы для каждой строки из исходного набора данных найти все строки, дата которых больше текущей, и среди них выбрать самую раннюю. Это и будет «дата окончания» или «дата начала следующего периода».
Предположим, наши данные находятся во временной таблице ВТ_Данные (подготовить их можно через Консоль запросов УФ от Dr.Zombi (удобно через расширенная консоль запросов для управляемых форм)). Рассмотрим текст запроса, который реализует эту логику:
ВЫБРАТЬ
ВТ.Ссылка КАК Ссылка,
ВТ.Дата КАК ДатаНачала,
МИНИМУМ(ВТ_След.Дата) КАК ДатаОкончания
ИЗ
ВТ_Данные КАК ВТ
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные КАК ВТ_След
ПО ВТ.Дата < ВТ_След.Дата
СГРУППИРОВАТЬ ПО
ВТ.Ссылка,
ВТ.Дата
Проанализируем по шагам, что происходит в этом запросе:
ВТ.Дата < ВТ_След.Дата заставляет систему для каждой записи найти все последующие записи.МИНИМУМ по полю даты из присоединенной таблицы идеально решает эту задачу.МИНИМУМ). Мы группируем результат по идентификатору (ссылке) и исходной дате.Важный нюанс: Для последней записи в таблице не найдется даты, которая была бы больше её собственной. В этом случае поле ДатаОкончания примет значение NULL. Чтобы «закрыть» последний период какой-то конкретной датой (например, концом времен или текущей датой), используйте функцию ЕСТЬNULL (это актуально и при использовании способа расчета остатков на каждый день).
Если вы работаете на современной версии платформы, использование соединений «самой с собой» является неоптимальным. Соединения по условию «больше/меньше» создают серьезную нагрузку на СУБД при больших объемах данных. Выясним, как сделать это проще с помощью оконных функций.
В языке запросов 1С появилась замечательная функция СЛЕДУЮЩЕЕ (аналог SQL-функции LEAD). С её помощью запрос становится линейным и выполняется значительно быстрее.
ВЫБРАТЬ
ВТ.Ссылка,
ВТ.Дата КАК ДатаНачала,
СЛЕДУЮЩЕЕ(ВТ.Дата) НАД(УПОРЯДОЧИТЬ ПО ВТ.Дата) КАК ДатаОкончания
ИЗ
ВТ_Данные КАК ВТ
Посмотрим на преимущества этого подхода:
НАД можно добавить ПАРТИЦИЯ ПО, если нужно строить диапазоны отдельно для каждого объекта (например, для каждой номенклатуры в регистре цен (поможет обработка очистки истории цен от избыточных записей)).Иногда задачу нужно решить не в «чистом» коде, а в отчете. В СКД есть мощный механизм работы с контекстом записей через вычисляемые выражения. Если мы хотим получить дату следующей строки, мы можем создать вычисляемое поле.
Разберем алгоритм настройки:
ДатаОкончания.ВычислитьВыражение("Дата", , , "Следующая", "Следующая")
Эта функция указывает системе взять значение поля Дата из следующей строки относительно текущей. Это самый «штатный» способ для отчетов, который не требует усложнения основного запроса набора данных.
Независимо от выбранного способа, следует помнить о нескольких критических моментах, которые могут повлиять на корректность и скорость работы:
1. Индексация: Если вы используете Способ №1 (соединение через временные таблицы), обязательно добавьте Индексировать по полю Дата (и по полям группировки, если они есть). Без индекса на больших таблицах запрос может выполняться недопустимо долго.
2. Одинаковые даты: Проанализируем ситуацию, когда две разные ссылки имеют абсолютно одинаковую дату и время. В этом случае условие ВТ.Дата < ВТ_След.Дата может вернуть некорректный интервал. Чтобы избежать этого, рекомендуется добавить в условие сравнение по уникальному идентификатору или использовать поле НомерСтроки (если данные предварительно пронумерованы).
3. Обработка последнего периода: Рассмотрим пример, когда нам нужно, чтобы последний период заканчивался не пустой датой, а определенным значением. В запросе это будет выглядеть так:
ВЫБРАТЬ
ВТ.Ссылка,
ВТ.Дата КАК ДатаНачала,
ЕСТЬNULL(СЛЕДУЮЩЕЕ(ВТ.Дата) НАД(УПОРЯДОЧИТЬ ПО ВТ.Дата), &ДатаКонца) КАК ДатаОкончания
ИЗ
ВТ_Данные КАК ВТ
Таким образом, мы выяснили, что наиболее эффективным и современным методом является использование оконных функций. Однако, знание классического метода с левым соединением остается необходимым для поддержки старых конфигураций и понимания базовых принципов работы реляционных запросов в 1С.