Как в запросе 1С получить себестоимость или цену на дату каждой продажи (срез последних на каждую дату)

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

Одной из наиболее распространенных задач в управленческом учете является расчет операционной прибыли. Основная сложность здесь заключается в необходимости сопоставить цену реализации из документа продажи с себестоимостью или другой расчетной ценой, которая хранится в периодическом регистре сведений на момент совершения сделки. В этой статье мы подробно разберем, как реализовать механизм «среза последних на каждую дату» в языке запросов 1С, выясним причины сложности этой задачи и проанализируем оптимальные пути её решения.

Почему нельзя использовать стандартную виртуальную таблицу СрезПоследних

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

Алгоритм получения среза последних на каждую дату пошагово

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

Шаг 1. Получение уникальных пар «Товар — Дата» из регистра продаж

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


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ПродажиОбороты.Номенклатура КАК Номенклатура,
    ПродажиОбороты.Период КАК ПериодПродажи
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК ПродажиОбороты
;

Шаг 2. Нахождение ближайшей даты записи в регистре цен

Теперь мы должны для каждой записи из нашей временной таблицы найти дату последнего изменения цены в регистре сведений, которая была МЕНЬШЕ или РАВНА дате продажи. Разберем этот момент подробнее: мы соединяем продажи с реальной таблицей регистра и используем группировку для поиска максимальной даты.


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

Шаг 3. Получение итоговых значений цен

На последнем этапе мы соединяем таблицу найденных дат (ВТ_ДатыЦен) обратно с реальной таблицей регистра сведений, чтобы получить ресурс (цену или себестоимость), соответствующий этой дате.


ВЫБРАТЬ
    ВТ_ДатыЦен.Номенклатура,
    ВТ_ДатыЦен.ПериодПродажи,
    ЦеныНоменклатуры.Цена КАК Себестоимость
ИЗ
    ВТ_ДатыЦен КАК ВТ_ДатыЦен
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
        ПО ВТ_ДатыЦен.Номенклатура = ЦеныНоменклатуры.Номенклатура
            И ВТ_ДатыЦен.ПериодЦены = ЦеныНоменклатуры.Период
;

Проблема производительности и пути оптимизации

При работе с большими объемами данных соединение по условию >= (неравенство) может существенно замедлить выполнение запроса. Проанализируем, как можно ускорить работу:

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

Альтернативный подход: Денормализация данных

В некоторых случаях, особенно при расчете себестоимости с учетом дополнительных расходов (как упоминалось в обсуждении), выгоднее использовать другой метод. Вместо сложного вычисления «на лету» в отчете, можно записывать рассчитанную себестоимость напрямую в регистр продаж в момент совершения операции или регламентной обработкой в конце дня. Рассмотрим преимущества:

Использование СКД для решения задачи

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

Таким образом, мы рассмотрели классический технический прием, который позволяет сопоставлять данные из разных регистров на произвольные моменты времени. Выбор конкретной реализации (через запрос, СКД или хранение данных в регистре накопления) зависит от требований к скорости работы системы и частоты обновления данных о себестоимости.

← На главную