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

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

В процессе разработки отчетов по анализу цен номенклатуры часто возникает задача сравнения текущей цены с предыдущим значением. Это необходимо для выявления динамики изменений: было ли снижение цены, когда это произошло и какой была старая цена. Давайте разберем, как эффективно решить эту задачу с помощью запросов к РегистрСведений.ЦеныНоменклатуры. Для быстрого получения данных без самостоятельного составления запросов можно использовать готовый отчет для анализа закупочных цен номенклатуры.

Подход 1: Использование группировок и поиск крайних периодов

Этот метод основан на определении двух последних дат изменения цен для каждой позиции номенклатуры. Мы группируем данные так, чтобы для каждого товара выявить максимальный и минимальный (в рамках двух последних) периоды, а затем соединяем их с физической таблицей регистра.

Рассмотрим реализацию данного метода по шагам:

  1. Находим Период для двух последних записей в разрезе номенклатуры.
  2. Сгруппируем данные для вычисления МАКСИМУМ(Период) (это будет текущая цена) и МИНИМУМ(Период) (предпоследняя цена).
  3. Соединим полученную временную таблицу с основным регистром для получения значений Цена.

ВЫБРАТЬ
    ТаблицаПериодов.Номенклатура КАК Номенклатура,
    ТаблицаПериодов.ПослПериод КАК ПослПериод,
    ПослЦеныНоменклатуры.Цена КАК ПослЦена,
    ТаблицаПериодов.ПредПериод КАК ПредПериод,
    ПредЦеныНоменклатуры.Цена КАК ПредЦена
ИЗ
    (ВЫБРАТЬ
        ЦеныНоменклатуры.Номенклатура КАК Номенклатура,
        МИНИМУМ(ЦеныНоменклатуры.Период) КАК ПредПериод,
        МАКСИМУМ(ЦеныНоменклатуры.Период) КАК ПослПериод
    ИЗ
        РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
    ГДЕ
        ЦеныНоменклатуры.ВидЦены = &ВидЦены
        И ЦеныНоменклатуры.Период В
                (ВЫБРАТЬ ПЕРВЫЕ 2
                    МАКСИМУМ(ЦеныКрайние.Период) КАК Период
                ИЗ
                    РегистрСведений.ЦеныНоменклатуры КАК ЦеныКрайние
                ГДЕ
                    ЦеныКрайние.Номенклатура = ЦеныНоменклатуры.Номенклатура
                    И ЦеныКрайние.ВидЦены = &ВидЦены
                СГРУППИРОВАТЬ ПО
                    ЦеныКрайние.Цена
                УПОРЯДОЧИТЬ ПО
                    Период УБЫВ))
    СГРУППИРОВАТЬ ПО
        ЦеныНоменклатуры.Номенклатура
    ИМЕЮЩИЕ
        МИНИМУМ(ЦеныНоменклатуры.Период) <> МАКСИМУМ(ЦеныНоменклатуры.Период)) КАК ТаблицаПериодов
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ПредЦеныНоменклатуры
        ПО (ТаблицаПериодов.Номенклатура = ПредЦеныНоменклатуры.Номенклатура)
            И (ПредЦеныНоменклатуры.ВидЦены = &ВидЦены)
            И (ПредЦеныНоменклатуры.Период = ТаблицаПериодов.ПредПериод)
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ПослЦеныНоменклатуры
        ПО (ТаблицаПериодов.Номенклатура = ПослЦеныНоменклатуры.Номенклатура)
            И (ПослЦеныНоменклатуры.ВидЦены = &ВидЦены)
            И (ПослЦеныНоменклатуры.Период = ТаблицаПериодов.ПослПериод)

Подход 2: Использование соединения со СрезПоследних

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

Важным аспектом является добавление условия на неравенство цены Ц.Цена <> ЦТекущие.Цена. Это позволяет пропускать записи, где цена не менялась, тем самым решая проблему «мусорных» периодов с одинаковой ценой.


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

Рекомендации по оптимизации

Чтобы отчеты работали быстро даже при большом объеме данных, проанализируем следующие моменты:

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

← На главную