При анализе данных часто возникает задача определить временные интервалы между операциями одного и того же контрагента. Например, нужно понять, как часто партнер совершает закупки: через 5 дней, через 10 или через месяц. В языке запросов 1С это не самая тривиальная задача, так как записи в базе данных хранятся независимо друг от друга, и «соседние» строки таблицы не имеют прямой связи. Рассмотрим несколько эффективных способов решения этой задачи, начиная от классических соединений и заканчивая современными инструментами платформы.
Это классический способ, который подходит для большинства версий платформы 1С. Основная идея заключается в том, чтобы для каждой записи найти «следующую» по времени запись того же партнера. Разберем этот процесс по шагам.
Шаг 1. Подготовка данных
Для начала нам необходимо поместить исходные данные во временную таблицу. Это критически важно для производительности, так как мы будем обращаться к этим данным несколько раз. Кроме того, мы сразу приведем даты к началу дня, если нам нужна разница именно в календарных днях.
ВЫБРАТЬ
Продажи.Партнер КАК Партнер,
НАЧАЛОПЕРИОДА(Продажи.Период, ДЕНЬ) КАК ДатаДок,
Продажи.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Данные
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода
;
Шаг 2. Поиск следующей даты
Теперь мы соединяем временную таблицу ВТ_Данные саму с собой. Условие соединения должно быть таким: тот же партнер, но дата во второй таблице строго больше, чем в первой. Чтобы найти именно ближайшую следующую дату, мы используем группировку и функцию МИНИМУМ.
ВЫБРАТЬ
Т1.Партнер КАК Партнер,
Т1.ДатаДок КАК ДатаТекущая,
МИНИМУМ(Т2.ДатаДок) КАК ДатаСледующая
ПОМЕСТИТЬ ВТ_СоседниеДаты
ИЗ
ВТ_Данные КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные КАК Т2
ПО Т1.Партнер = Т2.Партнер
И Т1.ДатаДок < Т2.ДатаДок
СГРУППИРОВАТЬ ПО
Т1.Партнер,
Т1.ДатаДок,
Т1.Ссылка
;
Шаг 3. Финальный расчет разницы
Проанализируем полученную таблицу. Теперь в каждой строке у нас есть текущая дата документа и дата следующего за ним документа. Нам остается только применить функцию РАЗНОСТЬДАТ.
ВЫБРАТЬ
Т.Партнер,
Т.ДатаТекущая,
Т.ДатаСледующая,
РАЗНОСТЬДАТ(Т.ДатаТекущая, Т.ДатаСледующая, ДЕНЬ) КАК ИнтервалВДнях
ИЗ
ВТ_СоседниеДаты КАК Т
ГДЕ
Т.ДатаСледующая ЕСТЬ НЕ NULL
В реальной базе возможна ситуация, когда у одного партнера есть два документа с абсолютно одинаковым временем. В этом случае условие Т1.ДатаДок < Т2.ДатаДок сработает некорректно (пропустит документы). Чтобы избежать этого, проанализируем ситуацию и добавим в условие соединения уникальный идентификатор (ссылку).
Изменим условие соединения на следующее:
ПО Т1.Партнер = Т2.Партнер
И (Т1.ДатаДок < Т2.ДатаДок ИЛИ (Т1.ДатаДок = Т2.ДатаДок И Т1.Ссылка < Т2.Ссылка))
Такой подход гарантирует, что система всегда найдет «следующий» документ, даже если время совпадает до секунды.
Если ваша задача — вывести эти данные в отчет, то использование сложных соединений в запросе может быть избыточным. Система компоновки данных (СКД) предоставляет мощный механизм ВычислитьВыражение, который работает гораздо быстрее на больших объемах данных.
Разберем, как настроить расчет в СКД:
РазницаДней.РАЗНОСТЬДАТ(ВычислитьВыражение("ДатаДок", , , "Предыдущая", "Предыдущая"), ДатаДок, "ДЕНЬ").ДатаДок, чтобы расчет шел последовательно.Этот метод позволяет избежать квадратичной сложности запроса, которая возникает при соединении таблицы «самой на себя».
В современных версиях платформы 1С реализована поддержка оконных функций прямо в языке запросов. Это самый прогрессивный метод. Мы можем использовать функцию LAG (или её эквивалент через синтаксис окон), чтобы получить значение из предыдущей строки.
Пример логики запроса с оконной функцией (доступно в новых релизах):
ВЫБРАТЬ
Продажи.Партнер,
Продажи.Период,
РАЗНОСТЬДАТ(
ВЫЧИСЛИТЬВЫРАЖЕНИЕ("Период", "ПАРТНЕР", "ПЕРИОД ВОЗРАСТАНИИ", "1 ПРЕДЫДУЩАЯ"),
Продажи.Период,
ДЕНЬ
) КАК Интервал
ИЗ
РегистрНакопления.Продажи КАК Продажи
Выясним, почему запросы с разностью дат могут тормозить базу данных. Если у одного партнера сотни или тысячи документов, обычное соединение без индексов превратит выполнение запроса в бесконечный процесс. Проанализируем правила оптимизации:
ИНДЕКСИРОВАТЬ ПО Партнер, ДатаДок.&НачалоПериода и &КонецПериода перед выполнением соединения.РАЗНОСТЬДАТ с параметром ДЕНЬ считает количество «границ» дат. Если один документ создан в 23:59, а второй в 00:01 следующего дня, результат будет равен 1, хотя прошло всего 2 минуты. Для получения точных календарных дней всегда используйте НАЧАЛОПЕРИОДА.Выбор конкретного метода зависит от версии вашей платформы и объема данных. Для небольших таблиц отлично подойдет Метод 1. Если вы строите отчет — используйте возможности СКД. Для высоконагруженных систем на актуальных версиях платформы отдавайте предпочтение оконным функциям.