Как вычислить разность между датами в соседних строках при выводе отчета в 1С

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

При разработке отчетов и обработок в системе 1С:Предприятие часто возникает задача анализа динамики данных. Одной из таких задач является вычисление интервала времени (например, просрочки) между двумя последовательными событиями — текущим и предыдущим платежом. Поскольку в языке запросов (до определенных версий платформы) и в стандартной выборке нет прямого обращения к "соседней" записи, разработчику приходится применять специальные алгоритмы.

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

Способ 1. Использование вспомогательной переменной в цикле выборки

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

Проанализируем алгоритм реализации этого метода:

  1. Объявляем переменную ПредыдущаяДата до начала цикла и инициализируем её значением Неопределено или пустой датой.
  2. Внутри цикла проверяем, заполнена ли эта переменная.
  3. Если переменная не заполнена (первая итерация), разность будет равна нулю.
  4. Если переменная заполнена, вычитаем из текущей даты (из выборки) значение нашей переменной.
  5. Важный момент: в конце каждой итерации цикла присваиваем переменной ПредыдущаяДата значение текущей даты.

Пример программного кода:


ПредыдущаяДата = Неопределено;

Пока Выборка.Следующий() Цикл
    Если ПредыдущаяДата = Неопределено Тогда
        // Это первая строка, сравнивать не с чем
        РазностьВДнях = 0;
    Иначе
        // В 1С разность дат возвращается в секундах
        // Делим на 86 400 (количество секунд в сутках)
        РазностьВДнях = (Выборка.ДатаПлатежа - ПредыдущаяДата) / 86400;
    КонецЕсли;

    // Выводим результат в область макета
    ОбластьСтрока.Параметры.ДатаПлатежа = Выборка.ДатаПлатежа;
    ОбластьСтрока.Параметры.Просрочка    = РазностьВДнях;
    ТабДок.Вывести(ОбластьСтрока);

    // Запоминаем текущую дату для следующего шага цикла
    ПредыдущаяДата = Выборка.ДатаПлатежа;
КонецЦикла;

Обратите внимание, что при простом вычитании Дата1 - Дата2 результат всегда получается в секундах. Чтобы получить целое количество дней, можно использовать функцию Цел() или Окр().

Способ 2. Вычисление разности в Системе компоновки данных (СКД)

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

Рассмотрим, как настроить такое поле в схеме компоновки данных:

  1. Перейдите на закладку "Вычисляемые поля".
  2. Добавьте новое поле, например, РазницаДней.
  3. В колонке "Выражение" укажите следующую конструкцию:

РазностьДат(ВычислитьВыражение("ДатаПлатежа", , , "Предыдущая", "Предыдущая"), ДатаПлатежа, "День")

Разберем подробнее параметры функции ВычислитьВыражение в данном контексте:

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

Способ 3. Оконные функции в запросах (Платформа 8.3.20+)

Для разработчиков, использующих современные версии платформы (начиная с 8.3.20), доступен наиболее эффективный способ — оконные функции прямо в языке запросов. Это позволяет переложить расчеты на сторону СУБД, что значительно ускоряет выполнение при больших объемах данных.

Проанализируем, как выглядит такой запрос:


ВЫБРАТЬ
    Продажи.Дата КАК ТекущаяДата,
    ПРЕДЫДУЩЕЕ(Продажи.Дата) КАК ПредыдущаяДата,
    РАЗНОСТЬДАТ(ПРЕДЫДУЩЕЕ(Продажи.Дата), Продажи.Дата, ДЕНЬ) КАК РазницаДней
ИЗ
    Документ.РеализацияТоваровУслуг КАК Продажи
УПОРЯДОЧИТЬ ПО
    Продажи.Дата

Использование ключевого слова ПРЕДЫДУЩЕЕ (аналог LAG в SQL) позволяет получить значение из предшествующей записи без использования циклов или сложных соединений. Выясним причину, почему это удобно: вы сразу получаете готовую таблицу с вычисленными интервалами, которую можно выгрузить в ТЗ или вывести в отчет.

Способ 4. Соединение таблицы самой с собой (для старых версий)

Если ваша платформа не поддерживает оконные функции и вы не используете СКД, задачу можно решить классическим приемом "самосоединения" в запросе. Выясним, как это работает по шагам:

  1. Сначала мы выбираем данные во временную таблицу и добавляем к ним порядковый номер. Для этого можно использовать автонумерацию или соединение по условию Т1.Дата > Т2.Дата с подсчетом количества записей.
  2. Затем мы соединяем эту временную таблицу саму с собой по условию Т1.НомерСтроки = Т2.НомерСтроки + 1.
  3. В результате в одной строке запроса у нас оказываются данные текущей строки (Т1) и предыдущей (Т2).

Посмотрим на пример такого запроса:


// Сначала подготавливаем таблицу с индексами (номерами строк)
ВЫБРАТЬ
    Платежи.Дата КАК Дата,
    АВТОНОМЕРЗАПИСИ() КАК НомерСтроки
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    РегистрНакопления.Взаиморасчеты КАК Платежи;

// Соединяем таблицу с её копией со смещением индекса на 1
ВЫБРАТЬ
    Т1.Дата КАК ТекущаяДата,
    Т2.Дата КАК ПредыдущаяДата,
    РАЗНОСТЬДАТ(Т2.Дата, Т1.Дата, ДЕНЬ) КАК ДнейМеждуПлатежами
ИЗ
    ВТ_Данные КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные КАК Т2 ПО Т1.НомерСтроки = Т2.НомерСтроки + 1

Важное примечание: Использование АВТОНОМЕРЗАПИСИ() доступно не во всех контекстах, иногда номер строки приходится вычислять через коррелированные подзапросы или временные таблицы, что может быть ресурсозатратно.

Полезные советы при работе с датами

При реализации любого из перечисленных выше методов следует учитывать несколько важных нюансов:

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

← На главную