При построении сложных аналитических отчетов по продажам часто возникает задача сопоставить движения регистра накопления с актуальными ценами номенклатуры. Стандартная виртуальная таблица РегистрСведений.ЦеныНоменклатуры.СрезПоследних принимает в качестве параметра периода фиксированную дату (например, дату окончания отчета). Если же нам требуется узнать цену, действовавшую непосредственно в момент совершения каждой конкретной продажи (на дату регистратора), передать поле строки во входящий параметр виртуальной таблицы не удастся — платформа 1С этого не поддерживает.
Выясним причину этого ограничения, разберем типовые ошибки в исходном запросе и рассмотрим два надежных способа решения задачи: через временные таблицы и через механизм связей наборов данных в СКД.
Параметры виртуальных таблиц в языке запросов 1С вычисляются СУБД до выполнения основного соединения. В параметры виртуальной таблицы СрезПоследних(&Период, ...) можно передавать только константы или внешние параметры запроса, но нельзя передать поле другой таблицы (например, ПродажиОбороты.Период или ПродажиОбороты.Регистратор.Дата).
Кроме того, проанализируем важную ошибку в исходном запросе автора:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаКон, ) КАК ЦеныНоменклатурыСрезПоследних
ПО ПродажиОбороты.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура
...
ГДЕ
ЦеныНоменклатурыСрезПоследних.ТипЦен = &ТипЦен
Важный момент: условие по присоединенной таблице в секции ГДЕ неявно превращает ЛЕВОЕ СОЕДИНЕНИЕ во внутреннее (INNER JOIN). Если для какой-либо номенклатуры цена заданного типа не найдена, вся строка продажи пропадет из отчета. Условие по типу цен следует переносить либо в блок ПО, либо в параметры виртуальной таблицы: СрезПоследних(&Дата, ТипЦен = &ТипЦен).
Если отчет строится с помощью обычного запроса или построителя, классическим решением является трехэтапная выборка с использованием временных таблиц. Разберем этот алгоритм по шагам:
РегистрСведений.ЦеныНоменклатуры по условию, что дата записи регистра меньше либо равна дате документа, и вычисляем МАКСИМУМ(Период).Посмотрим на практическую реализацию этого механизма в коде запроса:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ПродажиОбороты.Номенклатура КАК Номенклатура,
НАЧАЛОПЕРИОДА(ПродажиОбороты.Период, ДЕНЬ) КАК Период
ПОМЕСТИТЬ ВТ_ТоварыИДаты
ИЗ
РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон, Регистратор, ) КАК ПродажиОбороты
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
Период
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Номенклатура КАК Номенклатура,
ВТ.Период КАК ПериодДокумента,
МАКСИМУМ(Цены.Период) КАК ПериодЦены
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
ВТ_ТоварыИДаты КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены
ПО ВТ.Номенклатура = Цены.Номенклатура
И Цены.Период <= ВТ.Период
И Цены.ТипЦен = &ТипЦен
СГРУППИРОВАТЬ ПО
ВТ.Номенклатура,
ВТ.Период
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
ПериодЦены
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
МаксДаты.Номенклатура КАК Номенклатура,
МаксДаты.ПериодДокумента КАК ПериодДокумента,
Цены.Цена КАК Цена
ПОМЕСТИТЬ ВТ_ЦеныНаКаждуюДату
ИЗ
ВТ_МаксимальныеДаты КАК МаксДаты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены
ПО МаксДаты.Номенклатура = Цены.Номенклатура
И МаксДаты.ПериодЦены = Цены.Период
И Цены.ТипЦен = &ТипЦен
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
ПериодДокумента
;
Теперь таблицу ВТ_ЦеныНаКаждуюДату можно легко связать левым соединением с основной таблицей продаж по полям Номенклатура и ПериодДокумента. Данный подход работает быстро даже на больших объемах данных, так как соединение по неравенству выполняется только для уникальных пар измерений, а не для сотен тысяч строк исходных движений.
Обратим внимание на периодичность регистра ЦеныНоменклатуры. В большинстве типовых конфигураций (например, «Управление торговлей» редакции 10.3 или «Управление производственным предприятием») регистр цен имеет периодичность День. Это означает, что время всех записей регистра приведено к значению 00:00:00.
Если дата регистратора содержит время (например, 15.05.2023 14:35:10), простое сравнение Цены.Период <= ВТ.Период корректно найдет цену, установленную в этот день. Однако для надежности и ускорения сопоставления рекомендуется нормализовать даты документов с помощью функции НАЧАЛОПЕРИОДА(ПродажиОбороты.Период, ДЕНЬ).
Если отчет строится на базе Системы компоновки данных (СКД), строить тяжелые пакеты временных таблиц не требуется. Платформа предоставляет штатную возможность передавать параметры в виртуальную таблицу для каждой строки другого набора данных.
Разберем настройку по шагам:
ВЫБРАТЬ
ПродажиОбороты.Номенклатура КАК Номенклатура,
ПродажиОбороты.Регистратор КАК Регистратор,
ПродажиОбороты.Период КАК Период,
ПродажиОбороты.КоличествоОборот КАК Количество,
ПродажиОбороты.СтоимостьОборот КАК Стоимость
ИЗ
РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон, Регистратор, ) КАК ПродажиОбороты
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
Цены.Цена КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ПериодСреза, ТипЦен = &ТипЦен) КАК Цены
НаборДанных1НаборДанных2Номенклатура = НоменклатураПериодСреза, а в выражении источника укажем Период (или Регистратор.Дата).При выполнении отчета СКД автоматически выполнит оптимизированный срез последних цен на дату каждой строки первого набора, избавив нас от необходимости вручную прописывать группировки и выборку максимальных дат.