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

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

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

Рассмотрим подробно несколько эффективных способов реализации этой логики: от классических соединений до современных оконных функций и механизмов СКД.

Способ 1: Использование временных таблиц и функции АВТОНОМЕРЗАПИСИ

Этот метод стал особенно актуальным с выходом версии платформы 8.3.13. Суть его заключается в том, чтобы пронумеровать строки во временной таблице, а затем соединить эту таблицу саму с собой по условию НомерСтроки = НомерСтроки + 1. Рассмотрим этот алгоритм по шагам.

Шаг 1. Подготовка и группировка данных. Сначала мы должны получить исходные данные и поместить их во временную таблицу. Важно, чтобы на этом этапе данные уже были сгруппированы, так как функция АВТОНОМЕРЗАПИСИ не может использоваться в одном запросе с СГРУППИРОВАТЬ ПО.


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

Шаг 2. Нумерация строк. Создаем вторую временную таблицу, добавляя поле с порядковым номером. Внимание: для корректной нумерации обязательно используйте предложение УПОРЯДОЧИТЬ ПО, иначе порядок номеров будет произвольным.


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

Шаг 3. Левое соединение таблицы самой с собой. Теперь мы можем обратиться к предыдущей строке, соединив текущую запись (Т1) с записью, чей номер на единицу меньше (Т2).


ВЫБРАТЬ
    Т1.Период,
    Т1.Сумма КАК ТекущаяСумма,
    ЕСТЬNULL(Т2.Сумма, 0) КАК ПредыдущаяСумма,
    ВЫБОР 
        КОГДА Т1.Сумма = 0 ТОГДА ЕСТЬNULL(Т2.Сумма, 0) 
        ИНАЧЕ Т1.Сумма 
    КОНЕЦ КАК СуммаСЗаполнением
ИЗ
    ВТ_СНомерами КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СНомерами КАК Т2 ПО Т1.НомерСтроки = Т2.НомерСтроки + 1

Способ 2: Соединение по условию неравенства периодов (Метод МАКСИМУМ)

Если версия платформы ниже 8.3.13 или вы не хотите полагаться на автонумерацию, можно использовать связь по времени или дате. Проанализируем ситуацию, когда нам нужно найти именно последнее ненулевое значение, которое могло быть несколько строк назад.

Разберем логику: для каждой текущей строки мы ищем все строки, период которых меньше текущего. Из этих строк мы выбираем ту, у которой период максимальный (то есть ближайшая к нам сверху).


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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Т1.Период,
    Т1.Регистратор,
    ВЫБОР 
        КОГДА Т1.ОтсрочкаПлатежаКоДню = 0 ТОГДА Т3.ОтсрочкаПлатежаКоДню 
        ИНАЧЕ Т1.ОтсрочкаПлатежаКоДню 
    КОНЕЦ КАК ИтоговаяОтсрочка
ИЗ
    ВТ_Взаиморасчеты КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СвязиПериодов КАК Т2 ПО Т1.Период = Т2.Период 
        И Т1.Регистратор = Т2.Регистратор
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Взаиморасчеты КАК Т3 ПО Т2.ПериодПредыдущейЗаписи = Т3.Период

Этот метод надежен, но требует осторожности при больших объемах данных. Соединение по условию "больше" (>) создает значительную нагрузку на SQL-сервер. Для оптимизации обязательно проиндексируйте поле Период во временных таблицах. Для оптимизированного вычисления разниц между смежными записями можно применить методику быстрого определения интервалов в запросе.

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

В современных версиях 1С появились полноценные оконные функции. Это самый производительный и элегантный способ. Для получения значения из предыдущей строки используется функция ПРЕДЫДУЩЕЕ (аналог LAG в SQL).

Рассмотрим пример синтаксиса:


ВЫБРАТЬ
    Т.Период,
    Т.Сумма КАК ТекущаяСумма,
    ПРЕДЫДУЩЕЕ(Т.Сумма) ОБЪЕДИНЯЮЩЕЕ (УПОРЯДОЧИТЬ ПО Т.Период) КАК ПредыдущаяСумма
ИЗ
    ВТ_Данные КАК Т

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

Способ 4: Механизмы СКД (ВычислитьВыражение)

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

Проанализируем параметры функции: ВычислитьВыражение("ИмяПоля", , , "Предыдущая", "Предыдущая").

  1. Первый параметр: Имя поля, значение которого нужно получить.
  2. Второй и третий параметры: Отвечают за группировку и область расчета (обычно оставляются пустыми для текущего контекста).
  3. Четвертый параметр ("Начало"): Указываем "Предыдущая".
  4. Пятый параметр ("Конец"): Также указываем "Предыдущая".

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

Рекомендации по производительности

Выясним, какой метод выбрать в зависимости от ситуации:

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

← На главную