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

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

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

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

Задача выбрать в запросе 1С последнюю строку возникает постоянно: нужен актуальный курс валюты, последняя цена номенклатуры, текущий статус документа или последнее движение по регистру. В языке запросов 1С нет отдельного оператора «ПОСЛЕДНЯЯ», поэтому задачу решают комбинацией стандартных конструкций — и у каждого подхода есть свои условия применения и подводные камни.

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

Критично понимать: «последняя строка» — это всегда последняя по какому-то критерию. Без явного упорядочивания или условия на максимальное значение понятие «последняя» не имеет смысла — СУБД не гарантирует физический порядок записей, и результат может меняться от выполнения к выполнению.

Способ 1: ПЕРВЫЕ 1 с упорядочиванием по убыванию

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

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

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

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

ИЗ

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

ГДЕ

Курсы.Валюта = &Валюта

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

Период УБЫВ

Здесь записи сортируются от самой поздней даты к ранней, и ПЕРВЫЕ 1 отсекает всё, кроме самой свежей записи. Если параметр Валюта не задан или по нему нет данных, запрос вернёт пустую выборку — это нормальное поведение, которое нужно обрабатывать в коде через Выборка.Следующий() или проверку Не Результат.Пустой().

Порядок ПЕРВЫЕ и УПОРЯДОЧИТЬ в тексте запроса не влияет на результат: платформа сначала сортирует, потом ограничивает. Но если забыть упорядочивание, ПЕРВЫЕ 1 вернёт произвольную строку — это самая частая причина «плавающих» ошибок, когда отчёт показывает то верные, то неверные данные.

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

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

ВЫБРАТЬ

Срез.Номенклатура КАК Номенклатура,

Срез.Цена КАК Цена

ИЗ

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

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

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

Способ 3: МАКСИМУМ и соединение с исходной таблицей

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

ВЫБРАТЬ

Движения.Номенклатура КАК Номенклатура,

МАКСИМУМ(Движения.Период) КАК ПоследнийПериод

ПОМЕСТИТЬ ВТ_Последние

ИЗ

РегистрНакопления.ТоварыНаСкладах КАК Движения

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

Движения.Номенклатура

;

ВЫБРАТЬ

Движения.Номенклатура,

Движения.Количество,

Движения.Регистратор

ИЗ

РегистрНакопления.ТоварыНаСкладах КАК Движения

ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Последние КАК Посл

ПО Движения.Номенклатура = Посл.Номенклатура

И Движения.Период = Посл.ПоследнийПериод

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

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

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

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

Подходит для любых таблиц и одиночного объекта. Требует явного упорядочивания, иначе результат случаен.

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

Только для периодических регистров сведений. Самый короткий и быстрый вариант, отбор — в параметрах таблицы.

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

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

Последняя строка табличной части документа

В табличных частях у строк есть поле НомерСтроки, и «последняя строка» определяется однозначно — это строка с максимальным номером. Здесь работает простой вариант с группировкой:

ВЫБРАТЬ

МАКСИМУМ(Товары.НомерСтроки) КАК НомерСтроки

ИЗ

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

ГДЕ

Товары.Ссылка = &Ссылка

Если нужны остальные поля этой строки, используйте ПЕРВЫЕ 1 с сортировкой НомерСтроки УБЫВ — так вы получите всю строку целиком одним запросом. Учтите, что номера строк в табличной части могут идти с пропусками после удаления строк, поэтому максимальный номер не обязательно равен количеству строк.

Порядок действий
  1. 1
    Определите критерий «последней» строки: дата, период, номер строки или регистратор.
  2. 2
    Для регистра сведений используйте СрезПоследних, для остального — ПЕРВЫЕ 1 с УПОРЯДОЧИТЬ ПО критерию УБЫВ.
  3. 3
    Добавьте отбор по объекту (валюта, номенклатура, документ), чтобы не сканировать лишнее.
  4. 4
    Выполните запрос в консоли запросов и проверьте, что возвращается именно ожидаемая запись.
  5. 5
    В коде обработайте случай пустого результата через Проверка Выборка.Следующий().

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

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

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

Результат = Запрос.Выполнить();

Если Не Результат.Пустой() Тогда

Выборка = Результат.Выбрать();

Выборка.Следующий();

ТекущийКурс = Выборка.Курс;

Иначе

ТекущийКурс = 0; // значение по умолчанию

КонецЕсли;

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

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

Упорядочивание
Норма

В запросе есть УПОРЯДОЧИТЬ ПО дате или номеру с УБЫВ

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

ПЕРВЫЕ 1 используется без сортировки

Отбор по объекту
Норма

Параметр отбора заполнен, запись находится

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

Параметр не задан — выборка пустая или случайная

Дубли по дате
Норма

На одну дату одна запись по объекту

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

Соединение возвращает несколько строк на объект

Обработка в коде
Норма

Есть проверка Результат.Пустой()

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

Поля читаются без проверки выборки

Типичные ошибки и ограничения

Чаще всего проблемы возникают не из-за синтаксиса, а из-за неверных предположений о данных. Знание типовых ловушек экономит часы отладки, особенно когда запрос «работал, а потом перестал».

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

Ещё одно ограничение касается итогов: конструкция ПЕРВЫЕ не работает в запросах с итогами по группировкам в сочетании с некоторыми обходами результата. Если нужен иерархический обход с итогами, выносите выборку последних строк в отдельный запрос или временную таблицу.

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

Итог

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

Последняя строка в запросе 1С выбирается через ПЕРВЫЕ 1 с сортировкой по убыванию либо через СрезПоследних для регистров сведений. Всегда задавайте явный критерий упорядочивания, помещайте отборы в параметры виртуальных таблиц и обрабатывайте пустой результат в коде.

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

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

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

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

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

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

Передайте список в параметр и используйте отбор «Номенклатура В (&Список)» в параметрах СрезПоследних или в секции ГДЕ перед группировкой. Запрос вернёт по одной последней записи на каждый товар из списка.

Почему запрос возвращает несколько строк, хотя нужна одна последняя?

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

Что вернёт запрос, если данных по отбору нет?

Пустую выборку — без ошибки. В коде это нужно обрабатывать явно: проверять Результат.Пустой() или возвращаемое значение Выборка.Следующий(), иначе обращение к полям выборки вызовет ошибку исполнения.

Можно ли взять последнюю строку результата обхода выборки в коде?

Да, но неэффективно: придётся перебрать все строки циклом, запоминая текущую. Правильнее изменить сам запрос — отсортировать по убыванию и взять ПЕРВЫЕ 1, чтобы СУБД вернула только нужную запись.