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