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