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