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