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

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

Как выбрать первое значение в запросе 1С

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

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

Выбор способа зависит от того, что именно понимается под «первым»: первая строка по заданной сортировке, максимальное значение поля или просто любая одна запись из набора. Неправильно выбранный приём приводит либо к лишним обращениям к базе, либо к непредсказуемому результату, когда «первое» значение оказывается случайным.

Ниже разобраны основные подходы с примерами кода, условиями применения и типичными ошибками. Все примеры применимы к управляемым и обычным формам на платформе 8.3, синтаксис запросов одинаков для большинства типовых конфигураций.

Конструкция ПЕРВЫЕ 1 — основной способ

Ключевое слово ПЕРВЫЕ ограничивает количество строк, возвращаемых запросом. Записывается сразу после ВЫБРАТЬ:

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

Товары.Ссылка КАК Товар,

Товары.Наименование КАК Наименование

ИЗ

Справочник.Номенклатура КАК Товары

ГДЕ

НЕ Товары.ПометкаУдаления

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

Пример: получить самый дорогой товар по таблице цен.

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

Цены.Номенклатура КАК Товар,

Цены.Цена КАК Цена

ИЗ

РегистрСведений.ЦеныНоменклатуры КАК Цены

ГДЕ

Цены.Номенклатура = &Номенклатура

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

Цена УБЫВ

Ключевое слово УБЫВ задаёт сортировку по убыванию, поэтому первой строкой станет запись с максимальной ценой. Если нужна минимальная цена — сортировку по убыванию просто убирают (по умолчанию сортировка возрастающая).

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

Самый частый практический сценарий — получить последнюю по времени запись: актуальную цену, курс валюты, кадровое назначение сотрудника. Здесь сортировка строится по полю периода или даты документа в порядке убывания.

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

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

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

ИЗ

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

ГДЕ

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

И Курсы.Период <= &ДатаКурса

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

Курсы.Период УБЫВ

Для документов сортировку по одной дате недостаточно: в пределах одной секунды может быть несколько документов. Надёжнее упорядочить по моменту времени — в запросе это делается через сортировку по ссылке с использованием функции МОМЕНТВРЕМЕНИ либо просто по полю Дата с добавлением Ссылка как второго критерия, если точность до секунды допустима.

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

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

Цены.Период УБЫВ,

Цены.Регистратор УБЫВ

Альтернатива: МАКСИМУМ и МИНИМУМ

Если «первое значение» означает «максимальное» или «минимальное», агрегатные функции часто проще и быстрее, чем сортировка с ограничением строк. Запрос сразу возвращает одно значение без выборки лишних данных:

ВЫБРАТЬ

МАКСИМУМ(Цены.Цена) КАК МаксЦена

ИЗ

РегистрСведений.ЦеныНоменклатуры КАК Цены

ГДЕ

Цены.Номенклатура = &Номенклатура

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

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

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

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

МАКСИМУМ / МИНИМУМ

Возвращает одно скалярное значение. Быстро и просто, но нельзя получить сопутствующие поля той же строки.

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

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

Обход выборки с Прервать

Гибкий программный вариант: первая строка читается в цикле, затем цикл прерывается. Удобен, когда логика сложнее одного запроса.

Программное получение первой строки выборки

Когда запрос уже написан и менять его нежелательно, первую строку можно взять программно — на стороне встроенного языка. Есть два типовых приёма.

Первый — обход выборки с немедленным прерыванием:

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1

| Товары.Ссылка КАК Товар

|ИЗ

| Справочник.Номенклатура КАК Товары

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

| Товары.Наименование";

Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда

НайденныйТовар = Выборка.Товар;

Иначе

НайденныйТовар = Неопределено;

КонецЕсли;

Второй — через выгрузку результата в таблицу значений и обращение к нулевой строке:

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

Если Результат.Количество() > 0 Тогда

НайденныйТовар = Результат[0].Товар;

КонецЕсли;

Обязательно проверяйте, что результат не пуст: обращение к Результат[0] при пустой таблице вызовет ошибку «Индекс находится за границами массива». Проверка Выборка.Следующий() в первом варианте выполняет ту же роль.

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

Первое значение в группировке: по одному на каждую группу

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

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

ВЫБРАТЬ

Док.Контрагент КАК Контрагент,

МАКСИМУМ(Док.Дата) КАК ПоследняяДата

ПОМЕСТИТЬ МаксДаты

ИЗ

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

ГДЕ

Док.Проведен

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

Док.Контрагент

;

ВЫБРАТЬ

Док.Контрагент КАК Контрагент,

Док.Ссылка КАК ПоследнийДокумент

ИЗ

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

ВНУТРЕННЕЕ СОЕДИНЕНИЕ МаксДаты КАК М

ПО Док.Контрагент = М.Контрагент

И Док.Дата = М.ПоследняяДата

Учтите: если у контрагента два документа с одинаковой датой, соединение вернёт оба. Для строго одного документа на группу добавьте во второй запрос группировку с МАКСИМУМ(Док.Ссылка) или дополнительный критерий по моменту времени.

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

Производительность и ограничения

Конструкция ПЕРВЫЕ 1 транслируется в TOP 1 на MS SQL Server и аналогичные ограничения на других СУБД. При наличии индекса по полю сортировки сервер может остановить сканирование после первой подходящей строки — это быстро. Без индекса потребуется полная сортировка выборки, что на больших таблицах ощутимо.

Несколько практических ограничений, о которых стоит помнить:

  • ПЕРВЫЕ нельзя применять во вложенных запросах и временных таблицах пакета — только в итоговом запросе выборки. Во вложенном запросе используйте агрегаты или перенесите ограничение на внешний уровень.
  • Сортировка по полям типа Строка неограниченной длины в запросе невозможна — такое поле нельзя использовать в УПОРЯДОЧИТЬ ПО.
  • При обращении к виртуальным таблицам регистров ПЕРВЫЕ 1 не отменяет необходимости задавать параметры виртуальной таблицы (период, условия) — без них выборка будет строиться по всей таблице.
  • В динамических списках форм ПЕРВЫЕ в произвольном запросе может конфликтовать с механизмами постраничного вывода — проверяйте поведение списка после изменения текста запроса.
Типичные ошибки
01
ПЕРВЫЕ 1 без УПОРЯДОЧИТЬ ПО — результат зависит от плана запроса и может измениться после обновления статистики СУБД.
02
Сортировка по дате документа без учёта одинаковых дат — возвращается не тот документ при совпадении времени.
03
Обращение к Результат[0] без проверки Количество() — ошибка «индекс за границами» на пустом результате.
04
Использование ПЕРВЫЕ во вложенном запросе — синтаксическая ошибка; ограничение допустимо только в итоговом запросе.
05
Сравнение даты без учёта границ периода: условие Период <= &Дата отсекает записи позже даты, что не всегда соответствует замыслу.

Проверка результата

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

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

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

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

Запрос с ПЕРВЫЕ 1 и сортировкой
Норма

Всегда возвращает одну и ту же строку при неизменных данных

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

Результат меняется между выполнениями — сортировка недетерминирована

Пустая выборка
Норма

Код корректно обрабатывает отсутствие строк (проверка Следующий() или Количество())

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

Ошибка обращения по индексу или Неопределено попадает в дальнейшую логику без контроля

Совпадение с МАКСИМУМ/МИНИМУМ
Норма

Значение первого поля совпадает с агрегатом по тому же условию

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

Расхождение — неверно выбрано направление сортировки или условие отбора

Часто задаваемые вопросы

Чем ПЕРВЫЕ 1 отличается от РАЗЛИЧНЫЕ?

ПЕРВЫЕ ограничивает количество возвращаемых строк, а РАЗЛИЧНЫЕ убирает дубликаты из всего результата. Это независимые конструкции: можно написать ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 1, чтобы получить одну уникальную строку, но чаще они решают разные задачи.

Можно ли выбрать первые N значений, а не одно?

Да, число после ПЕРВЫЕ произвольное: ПЕРВЫЕ 10 вернёт десять строк по заданной сортировке. Это стандартный приём для топ-списков — например, десяти самых продаваемых товаров.

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

Добавьте в условие отбор на заполненность: например, ГДЕ Товары.Артикул <> "" для строкового поля или проверку на NULL через НЕ ... ЕСТЬ NULL. После этого ПЕРВЫЕ 1 с сортировкой вернёт первую строку с заполненным значением.

Почему запрос с ПЕРВЫЕ 1 возвращает разные значения при каждом выполнении?

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

Что быстрее: ПЕРВЫЕ 1 с сортировкой или МАКСИМУМ?

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

Работает ли ПЕРВЫЕ в запросах к табличным частям документов?

Да, табличные части доступны в запросах как обычные таблицы (например, Документ.РеализацияТоваровУслуг.Товары), и ПЕРВЫЕ 1 с сортировкой по номеру строки вернёт первую строку табличной части.

Итог

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

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