Как в запросе 1С сравнить данные текущей строки со следующей?

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

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

Метод 1. Использование оконных функций (для версий 8.3.15 и выше)

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

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


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

Проанализируем этот код. В конструкции НАД (OVER) мы указываем УПОРЯДОЧИТЬ ПО, чтобы система понимала, какая именно строка считается «следующей» в контексте нашей задачи. Важно помнить, что для последней записи в выборке поле ДатаСледующейЗаписи примет значение NULL. Чтобы избежать проблем при дальнейших вычислениях, рекомендуется использовать функцию ЕСТЬNULL.

Метод 2. Использование временной таблицы и автонумерации

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

Шаг 1: Создаем временную таблицу с пронумерованными строками.


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

Шаг 2: Соединяем таблицу саму с собой по условию НомерСтроки = НомерСтроки + 1.


ВЫБРАТЬ
    Т1.Период КАК ДатаТекущая,
    Т2.Период КАК ДатаСледующая,
    Т1.Курс КАК КурсТекущий,
    Т2.Курс КАК КурсСледующий
ИЗ
    ВТ_ДанныеСНомером КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДанныеСНомером КАК Т2
    ПО Т1.НомерСтроки = Т2.НомерСтроки - 1

В данном примере мы сопоставляем строку с номером N со строкой N + 1. Это позволяет «подтянуть» данные из будущего в текущую строку. Использование ЛЕВОЕ СОЕДИНЕНИЕ гарантирует, что последняя строка не пропадет из результата, даже если для нее нет «следующей».

Метод 3. Классическое соединение по условию (SQL-подход)

Если данные не имеют строгой последовательности или нам нужно найти «ближайшее следующее» значение по дате (например, в разрезе номенклатуры), применяется метод соединения с группировкой. Посмотрим на пример кода:


ВЫБРАТЬ
    т1.Номенклатура,
    т1.Период КАК ДатаТекущая,
    МИНИМУМ(т2.Период) КАК ДатаСледующая
ПОМЕСТИТЬ ВТ_Интервалы
ИЗ
    РегистрСведений.ЦеныНоменклатуры КАК т1
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК т2
        ПО т1.Номенклатура = т2.Номенклатура
        И т1.Период < т2.Период
СГРУППИРОВАТЬ ПО
    т1.Номенклатура,
    т1.Период

Выясним причину использования функции МИНИМУМ. Когда мы соединяем таблицу по условию т1.Период < т2.Период, для одной текущей строки подбираются все строки, дата которых больше текущей. Чтобы выбрать именно ту, которая идет сразу за нашей, мы берем минимальную из всех найденных дат «будущего».

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

Нюансы работы с регистрами сведений

Когда мы работаем с периодическими регистрами сведений, важно учитывать измерения (ключи). Рассмотрим ситуацию: вы анализируете цены. Если вы просто найдете следующую дату в регистре, вы можете случайно получить дату установки цены для другого товара.

Поэтому при любом соединении (хоть через оконные функции, хоть через временные таблицы) необходимо добавлять условие по измерениям:

  1. В оконных функциях используйте ПАРТИЦИЯ ПО (например, СЛЕДУЮЩЕЕ(Период) НАД (ПАРТИЦИЯ ПО Номенклатура УПОРЯДОЧИТЬ ПО Период)).
  2. В соединениях всегда добавляйте равенство измерений в секцию ПО (т1.Номенклатура = т2.Номенклатура).

Обработка последней записи (NULL)

Проанализируем ситуацию с последней строкой выборки. Для нее «следующей» записи не существует, и результатом будет NULL. В задачах построения интервалов (например, для отчетов в СКД) этот NULL часто заменяют на «бесконечность» или текущую дату.

Пример использования ЕСТЬNULL для закрытия периода:


ВЫБРАТЬ
    т1.Период КАК ДатаНачала,
    ЕСТЬNULL(т2.СледующаяДата, ДАТАВРЕМЯ(3999, 12, 31)) КАК ДатаОкончания
ИЗ
    ...

Такой подход позволяет корректно использовать полученные данные в условиях запросов (например, для проверки вхождения даты в интервал через МЕЖДУ).

Подведем итог: выбор метода зависит от версии вашей платформы и объема данных. Для современных систем (8.3.15+) безусловным фаворитом являются оконные функции. Для старых систем оптимальным решением станет использование временной таблицы с автонумерацией. Тщательно следите за индексами по полям, участвующим в соединениях, чтобы обеспечить максимальную скорость работы запроса.

← На главную