Как правильно использовать функцию МАКСИМУМ в запросе 1С для получения последних цен?

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

При работе с периодическими регистрами сведений, такими как ЦеныНоменклатуры, разработчики часто сталкиваются с ситуацией, когда стандартная агрегатная функция МАКСИМУМ в запросе ведет себя «непредсказуемо». Кажется, что платформа игнорирует инструкцию и возвращает все записи регистра вместо одной последней. В этой статье мы подробно разберем, почему так происходит, как работает механизм группировки SQL и какими способами можно гарантированно получить актуальные данные на нужную дату.

Анализируем типичную ошибку группировки

Рассмотрим ситуацию: нам необходимо получить максимально свежую цену для каждой номенклатуры. Часто начинающий разработчик пишет запрос следующим образом:


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

Почему этот запрос не работает? Давайте выясним причину. В языке SQL (и в языке запросов 1С) секция СГРУППИРОВАТЬ ПО определяет уникальный ключ строки в итоговой выборке. Если вы включаете поле Цена в группировку, система будет искать максимальный период для каждой уникальной комбинации номенклатуры, характеристики и цены. Если цена менялась трижды в разные периоды, вы получите три строки, потому что каждая цена образует свою собственную группу.

Проанализируем ситуацию: функция МАКСИМУМ(Период) отрабатывает честно, но только внутри группы, где цена уже зафиксирована. Чтобы получить одну строку с последней ценой, поле Цена не должно участвовать в группировке, однако оно нам нужно в результате. Это классическая дилемма, которая решается несколькими способами.

Решение 1: Использование виртуальной таблицы СрезПоследних

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

Разберем по шагам, как это работает. Вместо того чтобы обращаться к физической таблице регистра, мы обращаемся к виртуальной:


ВЫБРАТЬ
    ЦеныНоменклатурыСрезПоследних.Номенклатура,
    ЦеныНоменклатурыСрезПоследних.Характеристика,
    ЦеныНоменклатурыСрезПоследних.Цена,
    ЦеныНоменклатурыСрезПоследних.Период
ИЗ
    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ВидЦены = &ВидЦены) КАК ЦеныНоменклатурыСрезПоследних

Преимущества этого метода:

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

Решение 2: Ручной срез через временные таблицы

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

Рассмотрим алгоритм действий:

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


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

Шаг 2. Определение максимального периода. Создаем вторую временную таблицу, где для каждой ключевой пары (Номенклатура + Характеристика) вычисляем только МАКСИМУМ(Период). Здесь мы не включаем цену в группировку!


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

Шаг 3. Получение ресурсов. Соединяем таблицу с максимальными датами обратно с таблицей всех цен по всем ключевым полям (Номенклатура, Характеристика и Период).


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

Технические нюансы работы функции МАКСИМУМ

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

Оптимизация и производительность

Посмотрим на ситуацию со стороны производительности. При использовании платформы 8.3.14 и выше, механизмы работы с временными таблицами были значительно оптимизированы. Однако стоит помнить: если вы используете метод с временными таблицами, обязательно индексируйте их перед соединением.

Добавление индекса во временную таблицу ВТ_МаксимальныеДаты по полям Номенклатура и МаксПериод позволит СУБД выполнить соединение мгновенно, даже если в справочнике сотни тысяч товаров. Без индекса система может прибегнуть к методу Nested Loops, что существенно замедлит выполнение запроса.

Подводя итог, можно сказать: функция МАКСИМУМ всегда работает корректно согласно законам логики SQL. Ошибка «неработающего максимума» — это почти всегда сигнал о том, что в секцию СГРУППИРОВАТЬ ПО попало лишнее поле, которое дробит выборку на мелкие группы. Используйте СрезПоследних для стандартных задач и многоуровневые запросы с временными таблицами для сложных аналитических отчетов.

← На главную