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