Как в запросе 1С получить одну строку с максимальным значением колонки для каждой группы?

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

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

Способ 1. Использование временной таблицы (имитация среза последних)

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

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

Пример реализации в коде запроса:


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

////////////////////////////////////////////////////////////////////////////////

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

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

Способ 2. Оконные функции (для платформы 8.3.20 и выше)

Если конфигурация работает на версии платформы 1С 8.3.20 и новее, рассмотрим более изящный и производительный способ. Мы можем использовать оконную функцию ПОРЯДКОВЫЙ_НОМЕР().

Этот механизм позволяет пронумеровать строки внутри каждой группы, отсортировав их так, чтобы строка с максимальным значением всегда имела номер 1.


ВЫБРАТЬ
    Данные.Контрагент,
    Данные.Договор,
    Данные.Документ,
    Данные.ПросроченоНа,
    ПОРЯДКОВЫЙ_НОМЕР() ОБРАБОТКА (РАСПРЕДЕЛЕНИЕ ПО Данные.Контрагент УПОРЯДОЧИТЬ ПО Данные.ПросроченоНа УБЫВ) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_Нумерация
ИЗ
    ВТ_ИсходныеДанные КАК Данные
;

ВЫБРАТЬ
    *
ИЗ
    ВТ_Нумерация
ГДЕ
    НомерСтроки = 1

Этот метод гарантированно оставляет только одну строку для каждого контрагента, избавляя нас от проблемы дублирования.

Способ 3. Использование коррелированного запроса в условии "ГДЕ"

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

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


ВЫБРАТЬ
    Данные.Контрагент,
    Данные.Договор,
    Данные.ПросроченоНа
ИЗ
    ВТ_ИсходныеДанные КАК Данные
ГДЕ
    Данные.ПросроченоНа = (
        ВЫБРАТЬ
            МАКСИМУМ(Вложенный.ПросроченоНа)
        ИЗ
            ВТ_ИсходныеДанные КАК Вложенный
        ГДЕ
            Вложенный.Контрагент = Данные.Контрагент
    )

Этот способ хорошо читается, но выясним его недостаток: он может работать медленнее, чем соединения с временными таблицами, если объем исходных данных велик. База данных будет выполнять вложенный запрос для каждой строки.

Способ 4. Конструкция ПЕРВЫЕ 1 и УПОРЯДОЧИТЬ

Часто разработчики пытаются решить задачу конструкцией ВЫБРАТЬ ПЕРВЫЕ 1 и сортировкой УПОРЯДОЧИТЬ ПО ПросроченоНа УБЫВ. Разберем, когда это работает.

Данный метод идеален, если вы делаете выборку только по одному конкретному контрагенту (например, при печати документа или в обработке, где контрагент передан как параметр). Но если вам нужен список или отчет по всем контрагентам базы, ПЕРВЫЕ 1 оставит только одну, самую максимальную строку во всей базе, отбросив остальных контрагентов. Для групповых отчетов этот метод применять нельзя.

Способ 5. Решение задачи в настройках СКД

Если вы разрабатываете отчет на Системе Компоновки Данных (СКД) и не хотите усложнять запрос, можно решить задачу силами самой компоновки:

  1. В запросе просто выберите все данные без агрегации.
  2. Перейдите на вкладку "Ресурсы".
  3. Добавьте нужное поле (например, ПросроченоНа) в ресурсы и укажите функцию Максимум(ПросроченоНа).
  4. Если помимо этого поля нужно вывести подчиненные реквизиты (например, договор), можно использовать функцию СКД ВычислитьВыражение(), однако это требует аккуратной настройки группировок.

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

← На главную