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