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