К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как выбрать последнюю дату в запросе 1С

Способы выбрать последнюю дату в запросе 1С: агрегатная функция МАКСИМУМ, сортировка по убыванию с ПЕРВЫЕ 1, виртуальная таблица СрезПоследних. Примеры кода и типичные ошибки.

Задача получить последнюю дату встречается в разработке на платформе 1С постоянно: найти дату последнего документа контрагента, определить актуальную цену товара, узнать, когда сотрудник в последний раз выходил на работу. В языке запросов 1С для этого есть три основных подхода — агрегатная функция МАКСИМУМ, сортировка по убыванию с ограничением выборки и виртуальные таблицы регистров сведений.

Выбор способа зависит от того, что именно требуется: просто значение максимальной даты, целая строка с этой датой или «срез» актуальных значений по каждому измерению. Ошибка в выборе метода приводит либо к неверным данным (например, дата от одной строки, а сумма — от другой), либо к лишней нагрузке на СУБД.

Ниже разобраны все три подхода с примерами текста запроса, условиями применения, способами проверки результата и типичными ошибками, которые допускают даже опытные разработчики.

Получение максимальной даты через функцию МАКСИМУМ

Агрегатная функция МАКСИМУМ — самый прямой способ, когда нужна только сама дата, а не связанные с ней данные. Она работает в связке с предложением СГРУППИРОВАТЬ ПО либо без группировки, если требуется единственное значение по всей таблице.

ВЫБРАТЬ

МАКСИМУМ(РеализацияТоваровУслуг.Дата) КАК ПоследняяДата

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

ГДЕ

РеализацияТоваровУслуг.Проведен

Если нужна последняя дата по каждому контрагенту, добавьте группировку:

ВЫБРАТЬ

РеализацияТоваровУслуг.Контрагент КАК Контрагент,

МАКСИМУМ(РеализацияТоваровУслуг.Дата) КАК ПоследняяДата

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

ГДЕ

РеализацияТоваровУслуг.Проведен

СГРУППИРОВАТЬ ПО

РеализацияТоваровУслуг.Контрагент

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

Выбор строки с последней датой через ПЕРВЫЕ 1 и сортировку

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

ВЫБРАТЬ ПЕРВЫЕ 1

РеализацияТоваровУслуг.Ссылка КАК Документ,

РеализацияТоваровУслуг.Дата КАК Дата,

РеализацияТоваровУслуг.СуммаДокумента КАК Сумма

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

ГДЕ

РеализацияТоваровУслуг.Контрагент = &Контрагент

И РеализацияТоваровУслуг.Проведен

УПОРЯДОЧИТЬ ПО

Дата УБЫВ

Здесь есть тонкость с документами: у нескольких документов дата может совпадать с точностью до секунды. Поле Дата у документа — это комбинация даты и времени, но коллизии возможны. Чтобы выбор был однозначным, добавьте в сортировку ссылку:

УПОРЯДОЧИТЬ ПО

РеализацияТоваровУслуг.Дата УБЫВ,

РеализацияТоваровУслуг.Ссылка УБЫВ

Порядок сортировки по ссылке соответствует хронологии в пределах одной секунды для большинства конфигураций, поэтому такой приём даёт детерминированный результат. Конструкция ПЕРВЫЕ 1 транслируется в ограничение выборки на уровне СУБД (TOP в MS SQL, LIMIT в PostgreSQL), поэтому работает быстро при наличии индекса по дате.

Срез последних для регистров сведений

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

ВЫБРАТЬ

ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,

ЦеныНоменклатурыСрезПоследних.Цена КАК Цена,

ЦеныНоменклатурыСрезПоследних.Период КАК Период

ИЗ

РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ВидЦены = &ВидЦены) КАК ЦеныНоменклатурыСрезПоследних

Параметры виртуальной таблицы указываются в скобках: первый — момент среза (если опустить, берётся срез на текущую максимальную дату), второй — условие отбора по измерениям и реквизитам. Отбор внутри параметров виртуальной таблицы эффективнее, чем отбор в секции ГДЕ, потому что срез строится сразу по ограниченному набору измерений.

Если виртуальная таблица недоступна (например, регистр непериодический, а «последняя дата» хранится в реквизите), используют связку МАКСИМУМ во вложенном запросе с последующим соединением — об этом следующий раздел.

Соединение с максимальной датой для получения полной строки

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

ВЫБРАТЬ

Курсы.Валюта КАК Валюта,

Курсы.Период КАК Период,

Курсы.Курс КАК Курс

ИЗ

РегистрСведений.КурсыВалют КАК Курсы

ВНУТРЕННЕЕ СОЕДИНЕНИЕ (

ВЫБРАТЬ

КурсыВалют.Валюта КАК Валюта,

МАКСИМУМ(КурсыВалют.Период) КАК МаксПериод

ИЗ

РегистрСведений.КурсыВалют КАК КурсыВалют

СГРУППИРОВАТЬ ПО

КурсыВалют.Валюта) КАК Максимумы

ПО Курсы.Валюта = Максимумы.Валюта

И Курсы.Период = Максимумы.МаксПериод

Этот приём корректен, но имеет слабое место: если на одну и ту же дату (период) по одному измерению существует несколько записей, соединение вернёт их все — итоговая выборка «раздуется». В периодических регистрах сведений платформа обычно не допускает двух записей с одинаковым периодом и измерениями, а вот в документах или самописных регистрах такое возможно. В этих случаях добавляйте в условие соединения дополнительный уникальный признак или сворачивайте результат повторно.

Сравнение вариантов

МАКСИМУМ с группировкой

Подходит, когда нужна только дата или дата + показатели, которые можно агрегировать. Не возвращает связанные поля из той же строки.

ПЕРВЫЕ 1 + УПОРЯДОЧИТЬ УБЫВ

Подходит для одной строки по конкретному отбору (один контрагент, одна номенклатура). Неудобен, когда последняя запись нужна по многим группам сразу.

СрезПоследних

Штатный вариант для периодических регистров сведений: последняя запись по каждому измерению на заданную дату. Не применим к документам и непериодическим регистрам.

Соединение с МАКСИМУМ

Универсальный ручной способ для любых таблиц. Требует контроля дублей при совпадающих датах и обычно тяжелее по производительности.

Фильтрация по последней дате в отчётах на СКД и в динамических списках

В отчётах на СКД и динамических списках часто нужно не вывести последнюю дату, а отобрать записи за последний день, когда были движения. Типовое решение — вычислить максимальную дату во вложенном запросе и сравнить с ней начало дня текущей записи:

ВЫБРАТЬ

Продажи.Номенклатура КАК Номенклатура,

Продажи.Количество КАК Количество

ИЗ

РегистрНакопления.Продажи КАК Продажи

ГДЕ

НАЧАЛОПЕРИОДА(Продажи.Период, ДЕНЬ) = (

ВЫБРАТЬ

НАЧАЛОПЕРИОДА(МАКСИМУМ(П.Период), ДЕНЬ)

ИЗ

РегистрНакопления.Продажи КАК П)

Функции НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА здесь важны: регистраторы внутри дня имеют разное время, и сравнение периодов «в лоб» без приведения к началу дня отберёт только записи с точно совпадающей секундой.

В динамических списках с произвольным запросом помните, что сортировка, заданная в тексте запроса, может конфликтовать с пользовательской сортировкой и с ключом динамического считывания. Для списков, где требуется показать «последние» записи, надёжнее задать порядок через свойство списка, а не только через УПОРЯДОЧИТЬ в тексте.

Порядок действий при выборе способа
  1. 1
    Определите, что нужно на выходе: только дата, одна строка целиком или последние записи по каждой группе.
  2. 2
    Если источник — периодический регистр сведений, используйте СрезПоследних с отбором в параметрах.
  3. 3
    Если нужна одна строка по конкретному отбору — сортировка УБЫВ и ПЕРВЫЕ 1 с дополнительной сортировкой по ссылке.
  4. 4
    Если нужна только дата по группам — МАКСИМУМ с СГРУППИРОВАТЬ ПО.
  5. 5
    Проверьте результат на тестовых данных с дублями дат и с пустым набором записей.

Проверка результата и типичные ошибки

После написания запроса результат стоит проверить на контрольных признаках, а не только «глазами». Самый простой тест: вручную отсортировать таблицу в консоли запросов по дате убыв и сравнить первые строки с выдачей вашего запроса. Для срезов по группам выберите одну группу и сверьте её последнюю запись отдельным запросом с отбором.

Контрольные точки

Как проверить результат

Количество строк в выборке по группам
Норма

Число строк равно числу групп (измерений)

Тревожный признак

Строк больше, чем групп — значит, есть дубли дат и соединение размножило записи

Пустой источник данных
Норма

Запрос возвращает NULL или пустой результат без ошибки

Тревожный признак

Ошибка приведения типа при выгрузке NULL в переменную типа Дата

Совпадающие даты у документов
Норма

Запрос стабильно возвращает один и тот же документ при повторных выполнениях

Тревожный признак

Результат «плавает» — не хватает сортировки по ссылке

Отбор по периоду в срезе
Норма

Срез учитывает переданную дату и не выходит за неё

Тревожный признак

Возвращаются записи позже заданной даты — параметр среза не передан

Типичные ошибки
01
Использовать МАКСИМУМ по дате и рядом выводить неагрегированные поля — запрос либо не выполнится, либо поля попадут в группировку и размножат строки.
02
Сравнивать периоды без НАЧАЛОПЕРИОДА — записи с разным временем внутри дня не попадут в отбор.
03
Накладывать отбор на СрезПоследних через ГДЕ вместо параметров виртуальной таблицы — срез строится по всему регистру и запрос резко замедляется.
04
Забыть отбор по признаку Проведен у документов — «последней» может оказаться непроведённая или помеченная на удаление запись.
05
Использовать ПЕРВЫЕ 1 без УПОРЯДОЧИТЬ — СУБД вернёт произвольную строку, а не последнюю.

Отдельно предупредим о производительности: запросы с МАКСИМУМ по неиндексированному реквизиту даты на больших таблицах выполняют полное сканирование. Если такой запрос работает в массовой обработке или в условии соединения, имеет смысл вынести максимумы во временную таблицу с индексированием по полям соединения.

При отладке удобно сначала выполнить вложенный запрос с МАКСИМУМ отдельно и посмотреть его результат в консоли запросов. Если максимумы по группам определились правильно, а итоговый запрос даёт лишние строки — проблема точно в условиях соединения, а не в агрегации.
ПЗ
Практическое замечание

Частые вопросы

Как получить последнюю дату без группировки, просто одно значение?

Используйте ВЫБРАТЬ МАКСИМУМ(Дата) ИЗ <таблица> без предложения СГРУППИРОВАТЬ ПО. Результатом будет одна строка с одним полем. Учтите, что при пустой таблице значение будет NULL — обработайте этот случай функцией ЕСТЬNULL или проверкой в коде.

Чем СрезПоследних лучше ручного запроса с МАКСИМУМ?

Виртуальная таблица учитывает особенности хранения периодических регистров, корректно работает с параметром «на дату» и оптимизируется платформой. Ручной вариант с соединением проще ошибиться в условиях и сложнее оптимизировать; кроме того, СрезПоследних читается другими разработчиками однозначно.

Почему ПЕРВЫЕ 1 возвращает не ту запись?

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

Как выбрать последнюю дату по каждому контрагенту и сразу получить документ?

Сначала вложенным запросом получите пары «контрагент — максимальная дата», затем соедините с таблицей документов по контрагенту и дате. Если у контрагента несколько документов с одинаковой датой, результат будет содержать все они — при необходимости добавьте дополнительное ограничение по ссылке.

Работает ли МАКСИМУМ с полем Дата у документов с учётом времени?

Да, поле Дата документа включает время с точностью до секунды, и МАКСИМУМ сравнивает полное значение. Если нужно оперировать только календарным днём, предварительно приведите значение функцией НАЧАЛОПЕРИОДА(Дата, ДЕНЬ).

Что делать, если запрос с максимальной датой работает медленно?

Проверьте, есть ли индекс по полю даты и не накладывается ли отбор вне параметров виртуальной таблицы. Для сложных запросов вынесите расчёт максимумов во временную таблицу с индексом по полям соединения — это заметно сокращает повторные сканирования основной таблицы.

Итог

Что важно запомнить

Только дата — МАКСИМУМ с группировкой; одна строка целиком — ПЕРВЫЕ 1 с сортировкой УБЫВ и по ссылке; периодический регистр сведений — СрезПоследних с отбором в параметрах. Всегда проверяйте результат на дублях дат, пустом наборе и непроведённых документах.