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