В практике разработки на платформе 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
Если версия платформы ниже 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-сервер. Для оптимизации обязательно проиндексируйте поле Период во временных таблицах. Для оптимизированного вычисления разниц между смежными записями можно применить методику быстрого определения интервалов в запросе.
В современных версиях 1С появились полноценные оконные функции. Это самый производительный и элегантный способ. Для получения значения из предыдущей строки используется функция ПРЕДЫДУЩЕЕ (аналог LAG в SQL).
Рассмотрим пример синтаксиса:
ВЫБРАТЬ
Т.Период,
Т.Сумма КАК ТекущаяСумма,
ПРЕДЫДУЩЕЕ(Т.Сумма) ОБЪЕДИНЯЮЩЕЕ (УПОРЯДОЧИТЬ ПО Т.Период) КАК ПредыдущаяСумма
ИЗ
ВТ_Данные КАК Т
Преимущества: база данных выполняет этот расчет за один проход, не перемножая таблицы. Это избавляет от необходимости создавать сложные соединения и временные таблицы с индексами.
Если отчет строится на Системе компоновки данных, задачу можно решить без модификации запроса, используя «Вычисляемые поля». Функция ВычислитьВыражение позволяет обращаться к любым строкам группировки.
Проанализируем параметры функции: ВычислитьВыражение("ИмяПоля", , , "Предыдущая", "Предыдущая").
"Предыдущая"."Предыдущая".Это выражение вернет значение из строки, находящейся непосредственно над текущей. Если вам нужно получить значение из самой первой строки отчета, используйте "Первая" вместо "Предыдущая". Если требуется объединить данные сразу нескольких строк выборки, можно использовать способ сбора значений колонки из строк в СКД.
Выясним, какой метод выбрать в зависимости от ситуации:
ИНДЕКСИРОВАТЬ ПО для тех полей, по которым идет соединение (Период, НомерСтроки).МАКСИМУМ(Период), где условие соединения включает фильтр НЕ Значение = 0.Посмотрим на финальный совет: если ваша логика подразумевает сложные математические зависимости (например, результат текущей строки зависит от результата предыдущей, который сам был вычислен на основе своей предыдущей), запросы могут стать слишком громоздкими. В таких случаях эффективнее выгрузить результат запроса в ТаблицаЗначений и пройтись по ней циклом Для Каждого, сохраняя значение предыдущей итерации в переменную.