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

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

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

Почему нельзя просто соединить таблицу со срезом последних

В платформе 1С:Предприятие в параметры виртуальных таблиц (включая параметр &Период таблицы СрезПоследних) можно передавать только фиксированные значения, литералы или параметры всего запроса. Нельзя подставить поле строки Документ.Дата внутрь параметров виртуальной таблицы среза. Если же просто выполнить левое соединение с физической таблицей регистра с условием Период <= ДатаДокумента, мы получим не одну актуальную запись, а всю историю цен до указанной даты, что приведет к дублированию строк в результате.

Решение 1: Трехэтапный запрос с временными таблицами

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

  1. Формирование исходных данных. Получаем список документов, номенклатуры и дат документов, сохраняя их во временную таблицу.
  2. Поиск максимального периода. Соединяем исходные данные с физической таблицей регистра сведений по условию ПериодРегистра <= ДатаДокумента и с помощью агрегатной функции МАКСИМУМ(Период) находим дату последней установки цены для каждой связки номенклатуры и даты документа.
  3. Получение конечных данных регистра. Соединяем полученную таблицу максимальных периодов с физической таблицей регистра сведений по номенклатуре и точному совпадению найденного максимального периода, извлекая значение ресурса Цена.

Рассмотрим пример готового пакетного запроса, реализующего эту схему:


ВЫБРАТЬ
    ТабличнаяЧасть.Ссылка КАК ДокументСсылка,
    ТабличнаяЧасть.Ссылка.Дата КАК ДатаДокумента,
    ТабличнаяЧасть.Номенклатура КАК Номенклатура,
    СУММА(ТабличнаяЧасть.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_ДанныеДокументов
ИЗ
    Документ.Документ1.ТабличнаяЧасть1 КАК ТабличнаяЧасть
СГРУППИРОВАТЬ ПО
    ТабличнаяЧасть.Ссылка,
    ТабличнаяЧасть.Ссылка.Дата,
    ТабличнаяЧасть.Номенклатура
ИНДЕКСИРОВАТЬ ПО
    Номенклатура,
    ДатаДокумента
;

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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ДанныеДокументов.ДокументСсылка КАК ДокументСсылка,
    ДанныеДокументов.Номенклатура КАК Номенклатура,
    ДанныеДокументов.Количество КАК Количество,
    ЕСТЬNULL(ЦеныНоменклатуры.Цена, 0) КАК Цена
ИЗ
    ВТ_ДанныеДокументов КАК ДанныеДокументов
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_МаксимальныеПериоды КАК МаксимальныеПериоды
        ПО ДанныеДокументов.ДокументСсылка = МаксимальныеПериоды.ДокументСсылка
            И ДанныеДокументов.Номенклатура = МаксимальныеПериоды.Номенклатура
            И ДанныеДокументов.ДатаДокумента = МаксимальныеПериоды.ДатаДокумента
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РегистрСведений1 КАК ЦеныНоменклатуры
        ПО МаксимальныеПериоды.Номенклатура = ЦеныНоменклатуры.Номенклатура
            И МаксимальныеПериоды.ПериодЦены = ЦеныНоменклатуры.Период

Важные нюансы и оптимизация алгоритма

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

  1. Группировка во второй таблице. Обязательно группируем данные по полям Номенклатура и ДатаДокумента (или ссылке на документ). Если сгруппировать только по полю Номенклатура, запрос вернет одну общую максимальную дату за всю историю регистра, а не срез на момент проведения каждого документа.
  2. Дополнительные измерения регистра. Если в регистре сведений есть другие измерения (например, ВидЦены или Характеристика), их необходимо обязательно включать в условия соединений во второй и третьей таблицах пакета, иначе строки размножатся.
  3. Индексация временных таблиц. Для временных таблиц рекомендуется использовать директиву ИНДЕКСИРОВАТЬ ПО с перечислением ключевых полей связи. Это существенно ускоряет выполнение запроса на больших объемах данных.
  4. Обработка отсутствующих цен. Если для позиции на дату документа цена ни разу не устанавливалась, поиск периода вернет NULL. Рекомендуется использовать функцию ЕСТЬNULL(..., 0), чтобы корректно обрабатывать пустые значения.

Решение 2: Использование связей наборов данных в СКД

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

  1. Создаем первый набор данных — запрос к табличной части документов, выбирающий поля Ссылка, Номенклатура, Дата, Количество.
  2. Создаем второй набор данных — запрос к виртуальной таблице РегистрСведений.РегистрСведений1.СрезПоследних(&Период, Номенклатура = &Номенклатура), выбирающий Номенклатура и Цена.
  3. Переходим на вкладку «Связи наборов данных» и добавляем связь:
    • Источник связи: ПервыйНабор
    • Приемник связи: ВторойНабор
    • Условие связи: Номенклатура = Номенклатура
    • Параметры: в параметре Период указываем поле Дата из первого набора данных, а в параметре Номенклатура — поле Номенклатура.

В этом случае платформа автоматически выполнит срез последних на каждую дату для каждой конкретной строки, что делает схему компоновки простой и легкой в поддержке.

← На главную