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

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

При построении отчетов или обработке движений часто возникает задача: сопоставить строки документов закупки или реализации с актуальной ценой номенклатуры на момент проведения каждого конкретного документа. Если дата у всех документов разная, стандартное обращение к виртуальной таблице РегистрСведений.ЦеныНоменклатуры.СрезПоследних не срабатывает привычным образом.

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

Для решения этой классической задачи на практике используют обращение к реальной (физической) таблице регистра сведений. Разберем по шагам наиболее эффективные подходы.

Способ 1. Получение среза через временные таблицы и поиск максимального периода (рекомендуемый)

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

Рассмотрим подробнее реализацию этого алгоритма с помощью пакетного запроса:

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

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


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

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

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

Обратите внимание: если в регистре цен используются дополнительные измерения (например, ВидЦены или Характеристика), их необходимо обязательно включить во все условия соединений и группировок, чтобы избежать некорректного задвоения данных.

Способ 2. Соединение с проверкой на отсутствие более поздней записи (через ЕСТЬ NULL)

Разберем альтернативный способ без использования временных таблиц и функции МАКСИМУМ. Логика метода построена на двойном левом соединении с физической таблицей регистра цен.

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

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


ВЫБРАТЬ
    Закупки.Ссылка КАК ДокументЗакупки,
    Закупки.Дата КАК ДатаДокумента,
    Закупки.Номенклатура КАК Номенклатура,
    Цены1.Цена КАК Цена
ИЗ
    Документ.ПриобретениеТоваровУслуг.Товары КАК Закупки
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены1
        ПО Закупки.Номенклатура = Цены1.Номенклатура
            И Цены1.Период <= Закупки.Дата
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены2
        ПО Закупки.Номенклатура = Цены2.Номенклатура
            И Цены2.Период > Цены1.Период
            И Цены2.Период <= Закупки.Дата
ГДЕ
    Цены2.Период ЕСТЬ NULL

Важно: этот вариант выглядит компактнее, однако на больших выборках документов он может работать значительно медленнее Способа 1 из-за формирования промежуточного декартова произведения строк перед отсечением по ЕСТЬ NULL.

Способ 3. Использование механизма связей наборов данных в СКД

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

Разберем по шагам настройку схемы компоновки данных:

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

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

Особенности и тонкости оптимизации

При работе с физической таблицей периодического регистра сведений учитывайте следующие нюансы:

  1. Индексация: соединение по физической таблице будет выполняться быстро только в том случае, если СУБД использует подходящий кластерный индекс. Стандартный индекс регистра формируется по измерениям и периоду. По этой причине условие соединения по первому измерению (Номенклатура) в связке с оператором сравнения по Период обеспечивает оптимальную скорость выполнения.
  2. Периодичность регистра: если периодичность регистра сведений настроена как «По позиции регистратора» либо в течение одной секунды регистрируется несколько цен, группировки исключительно по полю Период может быть недостаточно. В таких ситуациях для однозначного определения актуальной записи в группировку и условия связей дополнительно добавляют поля Регистратор и НомерСтрокиЗаписи.
← На главную