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

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

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

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

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

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


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

Выясним причину, почему такой запрос возвращает NULL или некорректные данные:

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

Классический алгоритм: срез последних на каждую дату через физическую таблицу

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

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

Посмотрим на пример реализации запроса с использованием вложенного запроса:


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

Оптимизация производительности: использование временных таблиц

Рассмотрим важный аспект быстродействия. Использование вложенных запросов на больших объемах данных заставляет СУБД строить неоптимальные планы выполнения. В соответствии со стандартами разработки 1С рекомендуется разбивать алгоритм на временные таблицы с индексацией.

Разберем оптимизированный вариант:


// Шаг 1: Подготавливаем уникальные строки документов
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ПоступлениеТовары.Ссылка КАК Ссылка,
    ПоступлениеТовары.Номенклатура КАК Номенклатура,
    ПоступлениеТовары.Ссылка.ДатаВходящегоДокумента КАК ДатаСреза
ПОМЕСТИТЬ ВТ_ИсходныеДанные
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТовары
ГДЕ
    ПоступлениеТовары.Ссылка.Дата МЕЖДУ &Дата1 И &Дата2
ИНДЕКСИРОВАТЬ ПО
    Номенклатура,
    ДатаСреза
;

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

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

Важные практические нюансы

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

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