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

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

Часто перед разработчиком встает задача проанализировать динамику цен. Например, нужно создать отчет, который для каждой номенклатуры из документа «Заказ поставщику» покажет не только цену из этого документа, но и цены, которые действовали на начало и конец определенного периода. Это позволяет быстро оценить, как изменилась стоимость товара.

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

Решение 1: Прямое соединение с виртуальными таблицами

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

Почему именно левое соединение? Оно позволит нам сохранить все записи из основного источника (например, все изменения цен за период), даже если на начало или конец периода для какой-то номенклатуры цена еще не была установлена. В этом случае в соответствующих колонках будет значение NULL.

Проанализируем пример запроса, который решает эту задачу:


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

Разберем его по шагам:

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

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

Если данных очень много или источник номенклатуры — это не сам регистр, а, например, табличная часть документа, запрос из первого решения может работать медленно. В таких случаях лучше использовать временные таблицы.

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


// Помещаем нужную номенклатуру из документа во временную таблицу
ВЫБРАТЬ
    ЗаказПоставщикуТовары.Номенклатура,
    ЗаказПоставщикуТовары.Цена,
    &ТипЦен КАК ТипЦен
ПОМЕСТИТЬ ВТ_Номенклатура
ИЗ
    Документ.ЗаказПоставщику.Товары КАК ЗаказПоставщикуТовары
ГДЕ
    ЗаказПоставщикуТовары.Ссылка = &СсылкаНаДокумент
;

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


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

Этот подход более производителен, так как платформа сначала отбирает небольшой список номенклатуры, а уже потом по этому списку получает срезы, что значительно сокращает нагрузку.

Важные нюансы: СрезПоследних vs СрезПервых

В ходе обсуждения на форуме возник важный вопрос: какую виртуальную таблицу использовать — СрезПоследних или СрезПервых?

Таким образом, для получения цен на начало и конец периода всегда используйте СрезПоследних.

Решение 3: Использование нескольких наборов данных в СКД

Если отчет создается в Системе Компоновки Данных (СКД), задачу можно решить без написания сложного запроса, а с помощью объединения нескольких наборов данных.

  1. Набор данных 1 (Основные данные): Запрос, который получает список номенклатуры из документа или регистра.
  2. Набор данных 2 (Цена на начало): Запрос к виртуальной таблице РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&НачалоПериода, ).
  3. Набор данных 3 (Цена на конец): Запрос к виртуальной таблице РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&КонецПериода, ).

После создания наборов данных, на вкладке «Связи наборов данных» нужно настроить связи между ними по общим измерениям (Номенклатура, ТипЦен). СКД автоматически соединит данные, и вы сможете вывести все три цены в одном отчете. Этот способ удобен для быстрой разработки и дает большую гибкость в настройке отчета.

← На главную