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

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

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

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

Выясним причину появления дублей

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

В большинстве типовых конфигураций измерением регистра ЦеныНоменклатуры является не только Номенклатура, но и ТипЦен (или ВидЦены). Если в параметрах или условиях запроса указать условие ТипЦен <> &ТипЦен, система просто исключит конкретный тип цены (например, закупочную), но для всех остальных типов цен вернет актуальные на момент среза значения. В результате для одной позиции номенклатуры в выборку попадет столько строк, сколько активных типов цен хранится в регистре.

Решение с помощью пакетного запроса и временных таблиц

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

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

Рассмотрим пример пакетного запроса:


// Шаг 1: Получаем товары из документа
ВЫБРАТЬ
	УстановкаЦенНоменклатурыТовары.Номенклатура КАК Номенклатура,
	УстановкаЦенНоменклатурыТовары.ТипЦен КАК ТипЦен,
	УстановкаЦенНоменклатурыТовары.Цена КАК Цена
ПОМЕСТИТЬ ВТ_Док
ИЗ
	Документ.УстановкаЦенНоменклатуры.Товары КАК УстановкаЦенНоменклатурыТовары
ГДЕ
	УстановкаЦенНоменклатурыТовары.Ссылка = &Ссылка
	И УстановкаЦенНоменклатурыТовары.ТипЦен = &ТипЦенДокумента;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Находим максимальную дату установки цены по номенклатуре из документа
ВЫБРАТЬ
	Цены.Номенклатура КАК Номенклатура,
	МАКСИМУМ(Цены.Период) КАК Период
ПОМЕСТИТЬ ВТ_Периоды
ИЗ
	РегистрСведений.ЦеныНоменклатуры КАК Цены
ГДЕ
	Цены.Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ ВТ_Док.Номенклатура ИЗ ВТ_Док КАК ВТ_Док)
	И Цены.ТипЦен <> &ТипЦенИсключаемый
	И Цены.Регистратор <> &Ссылка
СГРУППИРОВАТЬ ПО
	Цены.Номенклатура;

////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Получаем максимальную цену на найденную максимальную дату
ВЫБРАТЬ
	Цены.Номенклатура КАК Номенклатура,
	Цены.Период КАК Период,
	МАКСИМУМ(Цены.Цена) КАК Цена
ПОМЕСТИТЬ ВТ_ПоследняяЦена
ИЗ
	ВТ_Периоды КАК Периоды
	ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены
		ПО Периоды.Номенклатура = Цены.Номенклатура
		И Периоды.Период = Цены.Период
ГДЕ
	Цены.ТипЦен <> &ТипЦенИсключаемый
	И Цены.Регистратор <> &Ссылка
СГРУППИРОВАТЬ ПО
	Цены.Номенклатура,
	Цены.Период;

////////////////////////////////////////////////////////////////////////////////
// Шаг 4: Сравниваем цену из документа с ранее установленной ценой
ВЫБРАТЬ
	Док.Номенклатура КАК Номенклатура,
	Док.Цена КАК ЦенаДокумента,
	ПоследняяЦена.Цена КАК ПредыдущаяЦена
ИЗ
	ВТ_Док КАК Док
	ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПоследняяЦена КАК ПоследняяЦена
		ПО Док.Номенклатура = ПоследняяЦена.Номенклатура
		И Док.Цена > ПоследняяЦена.Цена

Разберем логику работы этого пакета запросов:

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

Альтернативное решение с использованием оконных функций

В современной платформе 1С (начиная с версии 8.3.20) реализована поддержка оконных функций. Это позволяет существенно упростить выборку последней записи без создания цепочки промежуточных соединений.

Посмотрим на пример с использованием функции ROW_NUMBER():


ВЫБРАТЬ
	Цены.Номенклатура КАК Номенклатура,
	Цены.Цена КАК Цена,
	ROW_NUMBER() OVER(PARTITION BY Цены.Номенклатура ОБРАБОТКА ПО Цены.Период УБЫВ) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_РанжированныеЦены
ИЗ
	РегистрСведений.ЦеныНоменклатуры КАК Цены
ГДЕ
	Цены.Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ ВТ_Док.Номенклатура ИЗ ВТ_Док)
	И Цены.ТипЦен <> &ТипЦенИсключаемый
	И Цены.Регистратор <> &Ссылка;

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

В данном подходе конструкция PARTITION BY Цены.Номенклатура ORDER BY Цены.Период DESC нумерует все записи по каждой номенклатуре в порядке убывания даты. Самая свежая запись получает номер 1. Фильтрация по условию НомерСтроки = 1 гарантирует получение строго одной актуальной цены для каждой позиции номенклатуры.

← На главную