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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Архитектурная особенность выборки из результата запроса

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

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

Способ 1. Использование метода Следующий() внутри тернарного оператора

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

Посмотрим на пример реализации в одну строку с помощью тернарного оператора:


Выборка = РезультатЗапроса.Выбрать();
Значение = ?(Выборка.Следующий(), Выборка.Принтер, "");

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

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

Способ 2. Канонический вариант через условие

Проанализируем классическую конструкцию, которая считается стандартом надежного и читаемого кода:


Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
    Значение = Выборка.Принтер;
Иначе
    Значение = "";
КонецЕсли;

Разберем преимущества такого подхода перед компактными однострочниками:

  1. Простота отладки: разработчик всегда может поставить точку останова на строке присвоения и сразу увидеть состояние выборки в табло.
  2. Явная обработка отсутствия данных: в ветке Иначе можно не просто присвоить пустое значение, но и зафиксировать ошибку в журнале регистрации или выбросить исключение, если запись обязана присутствовать.
  3. Читаемость: код понятен любому специалисту без необходимости вчитываться в скрытую логику смещения курсора внутри условий.

Способ 3. Выгрузка в таблицу значений с индексным доступом

Посмотрим на альтернативный вариант, упомянутый на форуме, где используется метод Выгрузить():


Таблица = РезультатЗапроса.Выгрузить();
Значение = ?(Таблица.Количество() > 0, Таблица[0].Принтер, "");

Либо проверка через метод Пустой() самого результата:


Значение = ?(НЕ РезультатЗапроса.Пустой(), РезультатЗапроса.Выгрузить()[0].Принтер, "");

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

  1. Накладные расходы по памяти: метод Выгрузить() физически создает в оперативной памяти объект ТаблицаЗначений, инициализирует структуру ее колонок, создает строки и копирует данные.
  2. Нагрузка на сборщик мусора: создание полноценной таблицы ради чтения единственного значения одного поля создает избыточную нагрузку на серверную память. Использование легковесной выборки всегда эффективнее.

Способ 4. Оптимизация на уровне запроса СУБД

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |	яПринтерЭтикеток.Принтер КАК Принтер
    |ИЗ
    |	РегистрСведений.яПринтерЭтикеток КАК яПринтерЭтикеток
    |ГДЕ
    |	яПринтерЭтикеток.Пользователь = &Пользователь";

Запрос.УстановитьПараметр("Пользователь", ТекущийПользователь);
РезультатЗапроса = Запрос.Выполнить();

Выборка = РезультатЗапроса.Выбрать();
Значение = ?(Выборка.Следующий(), Выборка.Принтер, Неопределено);

Конструкция ПЕРВЫЕ 1 транслируется на уровне базы данных в инструкции TOP 1 (в MS SQL Server) или LIMIT 1 (в PostgreSQL). Это указывает СУБД прекратить поиск и сканирование индексов сразу после нахождения первого совпадения, сокращая нагрузку на дисковую подсистему и сетевой трафик между сервером СУБД и сервером приложений «1С:Предприятие».

Способ 5. Альтернатива без использования запроса

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


Отбор = Новый Структура("Пользователь", ТекущийПользователь);
ЗначенияРесурсов = РегистрыСведений.яПринтерЭтикеток.Получить(Отбор);
Значение = ЗначенияРесурсов.Принтер;

Разберем по шагам особенности этого метода:

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

Такой подход делает код лаконичным, избавляет от необходимости парсинга запроса платформой и гарантирует безопасное получение значений ресурсов.

← На главную