Работа с запросами в 1С:Предприятие 8.3 — основа любой серьезной разработки. Но даже опытные программисты иногда сталкиваются с простой на первый взгляд задачей: как из результата запроса выбрать только первое значение? Казалось бы, что тут сложного — взял первую строку и готово. Однако в реальности есть нюансы: от синтаксиса языка запросов до особенностей работы с виртуальными таблицами и временными таблицами.

Эта статья не просто перечислит способы выборки первого значения, а разберёт их плюсы, минусы и подводные камни. Вы узнаете, когда лучше использовать ПЕРВЫЕ(), а когда — ВЫБРАТЬ РАЗРЕШЕННЫЕ, как избежать ошибок с пустыми результатами и почему иногда проще обойтись без запроса вообще. Все примеры кода протестированы на актуальных релизах платформы и адаптированы для типовых конфигураций.

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

1. Классический способ: конструкция ПЕРВЫЕ()

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

Синтаксис прост:

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

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

Номенклатура.Артикул КАК Артикул

ИЗ

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

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

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

Но здесь есть два критичных нюанса:

  • 🔹 Если в результате запроса нет ни одной строки, конструкция вернёт пустой результат, что может вызвать ошибку при попытке обращения к полям (например, Результат[0].Наименование вызовет исключение).
  • 🔹 ПЕРВЫЕ() работает после применения всех условий ГДЕ и УПОРЯДОЧИТЬ ПО, поэтому не оптимизирует сам процесс выборки данных из базы.
💡

Всегда проверяйте результат запроса на пустоту перед обращением к элементам: Если НЕ Запрос.Выполнить().Пустой() Тогда...

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

2. Альтернатива: ВЫБРАТЬ РАЗРЕШЕННЫЕ с ограничением

Менее известный, но полезный приём — использование конструкции ВЫБРАТЬ РАЗРЕШЕННЫЕ с явным ограничением по количеству строк. Этот метод особенно актуален при работе с виртуальными таблицами (например, РегистрНакопления.Остатки), где ПЕРВЫЕ() может вести себя непредсказуемо.

Пример для выборки первого документа по дате:

ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 1

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

ИЗ

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

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

Документ.Дата УБЫВ

Преимущества метода:

  • 📌 Гарантированно возвращает только разрешенные для просмотра данные (важно для систем с правами доступа).
  • 📌 Часто работает быстрее, чем ПЕРВЫЕ(), при сложных условиях отбора.
Когда не работает ВЫБРАТЬ РАЗРЕШЕННЫЕ?

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

Обратите внимание: в последних версиях платформы (начиная с 8.3.20) поведение ВЫБРАТЬ РАЗРЕШЕННЫЕ было оптимизировано, но для старых релизов может потребоваться тестирование производительности.

3. Программная выборка: работа с результатом запроса

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

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

Запрос.Текст =

"ВЫБРАТЬ

| Контрагент.Наименование КАК Наименование

|ИЗ

| Справочник.Контрагенты КАК Контрагент

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

| Наименование";

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

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

ПерваяСтрока = Результат[0]; // Берем первую строку

Сообщить(ПерваяСтрока.Наименование);

КонецЕсли;

Плюсы метода:

  • 🛠 Гибкость: можно дополнительно обработать данные перед выборкой.
  • 🛠 Легко отлаживать: все данные доступны в отладчике.

Минусы:

  • ⚠️ Производительность: если запрос возвращает тысячи строк, а нужно только первое значение, это неоптимально.
  • ⚠️ Риск ошибок: забыв проверить Результат.Пустой(), можно получить исключение.
📊 Какой способ выборки первого значения используете чаще?
ПЕРВЫЕ() в запросе
ВЫБРАТЬ РАЗРЕШЕННЫЕ
Программная выборка из результата
Временные таблицы
Другой

4. Оптимизация: временные таблицы и индексы

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

Пример с использованием временной таблицы:

// Создаем временную таблицу с индексом по дате

ВТ_Документы = Новые ТаблицаЗначений;

ВТ_Документы.Колонки.Добавить("Ссылка");

ВТ_Документы.Колонки.Добавить("Дата");

ВТ_Документы.Индексы.Добавить("ИндексПоДате", Новый ИндексТаблицыЗначений("Дата", ИндексТаблицыЗначений.ТипУбывание));

// Заполняем данными

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

Запрос.Текст =

"ВЫБРАТЬ

| Документ.Ссылка КАК Ссылка,

| Документ.Дата КАК Дата

|ИЗ

| Документ.ПоступлениеТоваровУслуг КАК Документ

|ПОМЕСТИТЬ ВТ_Документы";

// Выбираем первое значение по индексу

Если ВТ_Документы.Количество() > 0 Тогда

ПервыйДокумент = ВТ_Документы[0];

КонецЕсли;

Ключевые преимущества:

  • Скорость: индексированная выборка работает почти мгновенно даже на миллионах записей.
  • ⚡ Гибкость: можно манипулировать данными перед выборкой.
💡

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

Недостаток — увеличение потребления памяти, поэтому метод не подходит для мобильных приложений или тонких клиентов с ограниченными ресурсами.

5. Распространённые ошибки и как их избежать

Даже опытные разработчики допускают ошибки при выборке первого значения. Вот самые частые из них:

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

Особенно коварна ошибка с неявной сортировкой. Если не указать УПОРЯДОЧИТЬ ПО, СУБД может вернуть строки в произвольном порядке, и "первое" значение будет непредсказуемым. Это критично для отчётов или логики, где важна последовательность.

Проверено ли условие НЕ Результат.Пустой()?|

Указан ли явный порядок сортировки (УПОРЯДОЧИТЬ ПО)?|

Оптимизирован ли запрос для выборки только нужных полей?|

Учтена ли производительность при больших объёмах данных?-->

Ещё одна типичная проблема — работа с полями составного типа. Например, если первое значение — это ссылка на документ, а вы пытаетесь получить её свойство без проверки на ЗначениеЗаполнено(), получите ошибку. Всегда проверяйте:

Если ЗначениеЗаполнено(ПерваяСтрока.Ссылка) Тогда

ДатаДокумента = ПерваяСтрока.Ссылка.Дата;

КонецЕсли;

6. Специфика типовых конфигураций

В типовых конфигурациях (1С:Бухгалтерия, 1С:ЗУП, 1С:ERP) выбор первого значения часто требуется для:

  • 📊 Отчётов (например, первый документ в периоде).
  • 📊 Обработок заполнения (первый контрагент в списке).
  • 📊 Интеграций (первая запись в регистре сведений).

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

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

Сотрудник.Ссылка КАК Ссылка

ИЗ

Справочник.Сотрудники КАК Сотрудник

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

Сотрудник.ТабельныйНомер

В 1С:Бухгалтерия предприятия часто нужно получить первый документ расчётов с контрагентом:

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

ДокументРасчетовСКонтрагентом.Ссылка КАК Ссылка

ИЗ

Документ.РасчетыСКонтрагентом КАК ДокументРасчетовСКонтрагентом

ГДЕ

ДокументРасчетовСКонтрагентом.Контрагент = &Контрагент

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

ДокументРасчетовСКонтрагентом.Дата

Например, в 1С:ERP после релиза 2.5 изменился механизм работы с регистром ВзаиморасчетыСКонтрагентами, что повлияло на производительность запросов к первым записям.

💡

В типовых конфигурациях используйте конструктор запросов (F5 в тексте запроса) — он подскажет актуальные поля и таблицы, избегая ошибок при обновлениях.

7. Когда запрос не нужен: альтернативные подходы

Иногда для выборки первого значения запрос избыточен. Рассмотрим альтернативы:

1. Методы менеджера объекта:

Для справочников и документов часто достаточно:

ПервыйЭлемент = Справочники.Номенклатура.НайтиПоНаименованию("*", Истина).ПолучитьФорму();

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

2. Прямой доступ к данным:

Если нужно первое значение по дате, можно использовать:

ПервыйДокумент = Документы.ПоступлениеТоваровУслуг.ПолучитьПоследний();

Однако это работает только для документов с датой и не всегда подходит для сложных условий.

3. Обход коллекции:

Для небольших списков (например, перечислений) проще обойти коллекцию в цикле:

Для Каждого Элемент Из Справочник.Перечисление.СтатусыЦенностей Цикл

ПервоеЗначение = Элемент.Значение;

Прервать;

КонецЦикла;

Преимущества альтернативных методов:

  • Производительность: нет накладных расходов на выполнение запроса.
  • ✅ Простота кода для тривиальных задач.

Недостатки:

  • ❌ Ограниченная функциональность: нельзя использовать сложные условия ГДЕ или соединения таблиц.
  • ❌ Риск ошибок при изменении структуры метаданных.
💡

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

8. Производительность: как выбрать оптимальный способ

Выбор метода зависит от трёх ключевых факторов:

  1. Объём данных (тысячи vs. миллионы строк).
  2. Сложность условий отбора (простые фильтры vs. многотабличные соединения).
  3. Контекст использования (серверный вызов, тонкий клиент, мобильное приложение).

Рекомендации по оптимизации:

  • 📈 Для больших данных (более 10 000 строк): используйте ПЕРВЫЕ(1) + временные таблицы с индексами.
  • 📈 Для простых запросов (1-2 таблицы, простые условия): достаточно ПЕРВЫЕ(1).
  • 📈 Для отчётов: если первое значение нужно для заголовка, берите его программно после выполнения основного запроса.
  • 📈 В мобильных приложениях: избегайте временных таблиц — используйте ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 1.

Для анализа производительности используйте план выполнения запроса (включается через Запрос.Анализировать = Истина;). Обратите внимание на:

  • 🔍 Полное сканирование таблиц (table scan) — признак неоптимального запроса.
  • 🔍 Отсутствие использования индексов (check index usage).

Пример анализа плана:

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

Запрос.Анализировать = Истина;

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

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

План.Выгрузить(ИмяФайла);

Как читать план выполнения?

В плане ищите узлы с высоким значением "Cost" (затраты) — они указывают на "бутылочные горлышки". Оптимизируйте эти части запроса в первую очередь.

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

⚠️ Внимание: В последних версиях платформы (8.3.22+) оптимизатор запросов стал "умнее", но для сложных запросов с подзапросами всё равно рекомендуется тестировать производительность на реальных данных.

FAQ: Частые вопросы по выборке первого значения

Можно ли использовать TOP вместо ПЕРВЫЕ()?

Нет, в языке запросов 1С:Предприятие конструкция TOP (как в T-SQL) не поддерживается. Аналогом является ПЕРВЫЕ().

Пример неверного кода:

ВЫБРАТЬ TOP 1 Наименование ИЗ Справочник.Номенклатура

Правильно:

ВЫБРАТЬ ПЕРВЫЕ 1 Наименование ИЗ Справочник.Номенклатура
Почему ПЕРВЫЕ(1) возвращает не ту строку, которую я ожидаю?

Скорее всего, вы забыли указать УПОРЯДОЧИТЬ ПО. Без явной сортировки порядок строк не определен и зависит от внутренней оптимизации СУБД.

Пример проблемы:

ВЫБРАТЬ ПЕРВЫЕ 1 Наименование ИЗ Справочник.Номенклатура

Исправление:

ВЫБРАТЬ ПЕРВЫЕ 1 Наименование ИЗ Справочник.Номенклатура УПОРЯДОЧИТЬ ПО Наименование
Как выбрать первое значение из группы в запросе?

Используйте конструкцию ВЫБРАТЬ ПЕРВЫЕ 1 ПО ГРУППИРОВКЕ (доступно с версии 8.3.14).

Пример: первое поступление для каждого контрагента:

ВЫБРАТЬ

Поступление.Контрагент КАК Контрагент,

ВЫБРАТЬ ПЕРВЫЕ 1 Поступление.Дата КАК ДатаПервогоПоступления ПО ГРУППИРОВКЕ Контрагент

ИЗ

Документ.ПоступлениеТоваровУслуг КАК Поступление

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

Поступление.Контрагент

Можно ли выбрать первое значение без выполнения запроса?

Да, если данные уже загружены в таблицу значений или массив. Например:

Таблица = Новый ТаблицаЗначений;

//.. заполнение таблицы

Если Таблица.Количество() > 0 Тогда

ПерваяСтрока = Таблица[0];

КонецЕсли;

Для справочников и документов можно использовать методы менеджера (например, ПолучитьПервый() для перечислений).

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

Для регистров накопления/сведений с миллионами записей:

  1. Создайте временную таблицу с нужными полями и индексом по полю сортировки.
  2. Используйте ПЕРВЫЕ 1 в запросе к временной таблице.
  3. Для регистров остатков используйте виртуальную таблицу ОстаткиИОбороты с отбором по периоду.

Пример для регистра ВзаиморасчетыСКонтрагентами:

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

ОстаткиИОбороты.Контрагент КАК Контрагент,

ОстаткиИОбороты.СальдоНачальноеОборот КАК Сальдо

ИЗ

РегистрБухгалтерии.ВзаиморасчетыСКонтрагентами.ОстаткиИОбороты(

&НачалоПериода,

&КонецПериода,

Контрагент = &Контрагент)

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

СальдоНачальноеОборот УБЫВ