Разработка и Конфигурация · 11 мин
Запрос 1С: как получить максимальное значение
Как в запросе 1С получить максимальное значение: функция МАКСИМУМ, группировка, максимум по нескольким полям, первые N записей. Примеры кода и типичные ошибки.
Задача «найти максимум в запросе» в 1С возникает постоянно: последняя цена продажи, максимальный остаток, самая свежая дата документа, наибольший номер строки. Язык запросов 1С даёт для этого несколько инструментов — агрегатную функцию МАКСИМУМ, конструкцию ПЕРВЫЕ N с сортировкой по убыванию и соединение таблицы с результатом группировки. Выбор зависит от того, нужно ли получить только само значение или всю строку, в которой оно находится.
Ключевая особенность, о которой часто забывают: МАКСИМУМ возвращает только значение поля, но не связанную с ним запись. Если нужно узнать, какой именно документ содержит максимальную цену или какой сотрудник получил максимальную премию, одной агрегатной функции недостаточно — потребуется группировка с последующим соединением или сортировка с отбором первых записей.
Функция МАКСИМУМ: базовый синтаксис
МАКСИМУМ — агрегатная функция языка запросов 1С. Она работает с числами, датами и строками (для строк максимум определяется по алфавитному порядку). Простейший пример — максимальная цена по всей таблице:
ВЫБРАТЬ
МАКСИМУМ(Цены.Цена) КАК МаксимальнаяЦена
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК Цены
Такой запрос вернёт одну строку с одним полем. Если таблица пустая, результат будет NULL — это нужно учитывать при обработке выборки в коде, иначе сравнение или арифметика с NULL даст неожиданный результат.
Аналогично находится максимальная дата — например, дата последнего проведённого документа по контрагенту. Для дат МАКСИМУМ работает корректно и возвращает самое позднее значение, что удобно для поиска «последнего» события.
Максимум в группах: СГРУППИРОВАТЬ ПО
Чаще всего максимум нужен не по всей таблице, а по каждому элементу группы: последняя цена по каждой номенклатуре, максимальный остаток по каждому складу. Для этого поля группировки выносятся в секцию СГРУППИРОВАТЬ ПО:
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
МАКСИМУМ(Цены.Период) КАК ПоследняяДата
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК Цены
СГРУППИРОВАТЬ ПО
Цены.Номенклатура
Этот запрос вернёт по одной строке на каждую номенклатуру с датой последней установки цены. Именно такой приём лежит в основе большинства отчётов «актуальные значения на дату».
При группировке по ссылочным полям можно добавить условие ИМЕЮЩИЕ — аналог ГДЕ, но применяемый к уже сгруппированным данным. Например, оставить только номенклатуру, у которой последняя цена установлена позже определённой даты:
ИМЕЮЩИЕ МАКСИМУМ(Цены.Период) > &ДатаОтсечки
Как получить всю строку с максимальным значением
Самая частая практическая задача: нужна не просто максимальная дата, а сама цена, действовавшая на эту дату, или документ, которым она установлена. Здесь есть два рабочих способа.
Соединение с результатом группировки
Первый способ — сгруппировать данные, найти максимум и соединить результат с исходной таблицей по полю максимума:
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
Цены.Цена КАК Цена,
Цены.Регистратор КАК Регистратор
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК Цены
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
Т.Номенклатура КАК Номенклатура,
МАКСИМУМ(Т.Период) КАК Период
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК Т
СГРУППИРОВАТЬ ПО
Т.Номенклатура) КАК Макс
ПО Цены.Номенклатура = Макс.Номенклатура
И Цены.Период = Макс.Период
У способа есть тонкость: если на одну и ту же дату (период) существует несколько записей, соединение вернёт их все. Для периодических регистров сведений с режимом «в пределах секунды» это редкость, но для самостоятельно созданных таблиц дубли возможны — тогда добавляют ещё один уровень максимума по регистратору или используют второй способ.
ПЕРВЫЕ 1 с сортировкой по убыванию
Второй способ — отсортировать таблицу по нужному полю по убыванию и взять первую строку:
ВЫБРАТЬ ПЕРВЫЕ 1
Продажи.Номенклатура КАК Номенклатура,
Продажи.Сумма КАК Сумма,
Продажи.Регистратор КАК Документ
ИЗ
РегистрНакопления.Продажи.Обороты КАК Продажи
УПОРЯДОЧИТЬ ПО
Продажи.Сумма УБЫВ
Этот вариант проще и гарантированно возвращает ровно одну строку, но работает только когда максимум нужен по всей таблице целиком, а не по каждой группе. Получить «первые N по каждой группе» одной конструкцией ПЕРВЫЕ нельзя — для этого применяют соединение с группировкой или обход результата в коде с контролем смены группы.
Сравнение подходов
Подходит, когда нужно только значение максимума по группам. Не возвращает связанные поля строки.
Возвращает всю строку с максимумом по каждой группе. Может дать дубли при совпадающих значениях максимума.
Самый простой способ получить одну запись с максимумом по всей таблице. Не работает для максимума внутри групп.
Максимум по нескольким условиям и виртуальные таблицы
При работе с регистрами накопления и сведений почти всегда выгоднее использовать виртуальные таблицы вместо физических. Например, для периодического регистра сведений «Цены номенклатуры» есть готовая виртуальная таблица СрезПоследних, которая уже возвращает актуальные значения на дату — по сути, решает задачу «максимум по периоду» без ручной группировки:
ВЫБРАТЬ
Срез.Номенклатура КАК Номенклатура,
Срез.Цена КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ) КАК Срез
Если максимум нужен с дополнительным отбором — например, последняя цена только по определённому типу цен — условие передаётся в параметр виртуальной таблицы или в секцию ГДЕ. Отбор в параметрах виртуальной таблицы выполняется быстрее, чем фильтрация результата через ГДЕ.
Для максимума по остаткам используют виртуальную таблицу Остатки, а для максимального оборота за период — Обороты с последующей группировкой и МАКСИМУМ по сумме или количеству. Попытка посчитать максимум напрямую по физической таблице регистра накопления приведёт к чтению всех движений и замедлит запрос на больших объёмах.
- 1Определите поле группировки (например, Номенклатура) и поле максимума (Период, Цена, Сумма)
- 2Напишите вложенный запрос: ВЫБРАТЬ поле группы, МАКСИМУМ(поле) ... СГРУППИРОВАТЬ ПО поле группы
- 3Соедините результат с исходной таблицей по полю группы и полю максимума
- 4Выведите нужные реквизиты строки: цену, регистратор, ответственного
- 5Проверьте результат на данных, где возможны дубли максимума, и при необходимости добавьте МАКСИМУМ по регистратору
Обработка NULL и особые случаи
Если в группе все значения поля равны NULL, МАКСИМУМ вернёт NULL. При левом соединении, где для части строк группы нет данных вообще, результат тоже будет NULL. Чтобы подставить значение по умолчанию, используют функцию ЕСТЬNULL:
ВЫБРАТЬ
Товары.Ссылка КАК Номенклатура,
ЕСТЬNULL(МАКСИМУМ(Цены.Цена), 0) КАК МаксимальнаяЦена
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены
ПО Товары.Ссылка = Цены.Номенклатура
СГРУППИРОВАТЬ ПО
Товары.Ссылка
Отдельный случай — максимум по составному типу поля. Если поле может хранить значения разных типов (например, строка и число), результат сравнения может быть неочевидным. Надёжнее привести значения к одному типу через ВЫРАЗИТЬ перед агрегацией.
В коде модуля при обходе результата запроса проверяйте значение на NULL через ЗначениеЗаполнено() или явное сравнение, прежде чем использовать его в арифметике или сравнениях — операции с NULL в 1С не вызывают ошибку, но дают не тот результат, который ожидает разработчик.
Производительность и проверка результата
Запрос с группировкой и соединением дважды обращается к одной таблице. На больших объёмах данных стоит проверить, какой вариант быстрее: соединение с группировкой, временная таблица с индексированием (конструкция ПОМЕСТИТЬ ... ИНДЕКСИРОВАТЬ ПО) или пакетный запрос. Индексация временной таблицы по полям соединения заметно ускоряет выборку.
Для проверки корректности запроса полезно выполнить его в консоли запросов на тестовых данных с заведомо известным результатом: создать две-три записи с разными значениями, убедиться, что возвращается именно максимальная, затем добавить дубль максимума и посмотреть поведение. Также проверьте пустую таблицу — запрос не должен падать, а код должен корректно обработать пустую выборку.
Как проверить результат
По одной строке на каждую группу
Несколько строк на группу — дубли максимума, нужна дополнительная обработка
NULL заменён через ЕСТЬNULL или осознанно оставлен
NULL попадает в расчёты и сравнения
Запрос отрабатывает быстро на рабочей базе
Долгое выполнение — проверьте виртуальные таблицы и индексацию временных таблиц
Частые вопросы
Можно ли получить максимум по двум полям одновременно?
Да, в одном запросе допустимо несколько агрегатных функций: МАКСИМУМ(Цена), МАКСИМУМ(Период). Но это два независимых максимума — они могут относиться к разным строкам. Если нужна строка, где максимальна цена, а из неё взять период, используйте соединение с группировкой.
Как найти N максимальных значений, а не одно?
Используйте ВЫБРАТЬ ПЕРВЫЕ N с сортировкой по нужному полю УБЫВ. Например, ПЕРВЫЕ 10 по сумме продаж даст топ-10 документов. Для «топ-N внутри каждой группы» одним запросом задача решается сложно — обычно применяют обход результата в коде со счётчиком по группе.
Почему МАКСИМУМ по строковому полу даёт неожиданный результат?
Строки сравниваются посимвольно по алфавиту, а не по числовому значению. Строка «9» окажется «больше» строки «10». Если в строковом поле хранятся числа, приведите их к числу через ВЫРАЗИТЬ или храните данные в поле числового типа.
Чем СрезПоследних лучше ручного МАКСИМУМ по периоду?
Виртуальная таблица СрезПоследних уже содержит актуальные значения на указанную дату и возвращает сразу всю строку регистра — и значение, и ресурсы, и регистратор. Ручная группировка с соединением повторяет ту же логику, но требует больше кода и чаще даёт ошибки с дублями.
Как получить максимум в запросе к табличной части документа?
Точно так же: ВЫБРАТЬ Ссылка, МАКСИМУМ(НомерСтроки) ИЗ Документ.РеализацияТоваровУслуг.Товары СГРУППИРОВАТЬ ПО Ссылка. Табличная часть в запросе ведёт себя как обычная таблица, к ней применимы все агрегатные функции.
Работает ли МАКСИМУМ в СКД и динамических списках?
Да, язык запросов в СКД и динамических списках тот же. В СКД агрегацию часто дополнительно настраивают на закладке «Ресурсы», где для поля указывают функцию Максимум — тогда итоги по группировкам отчёта считаются как максимум, а не сумма.
Что важно запомнить
МАКСИМУМ возвращает только значение поля и требует группировки всех остальных полей. Для получения всей строки с максимумом используйте соединение с результатом группировки или ПЕРВЫЕ 1 с сортировкой УБЫВ. Для периодических регистров сведений предпочтительна виртуальная таблица СрезПоследних. Всегда обрабатывайте NULL и проверяйте запрос на данных с дублями максимума.