В практике разработки на платформе 1С:Предприятие нередко возникают ситуации, когда по условиям бизнес-логики запрос возвращает ровно одну запись либо нам требуется получить только самое первое значение. Типичный пример — чтение индивидуальной настройки пользователя из регистра сведений. Разберем, как получить искомое значение компактно, почему привычный индексный доступ не работает напрямую с выборкой и какие варианты реализации являются наиболее правильными с точки зрения производительности и стандартов написания кода.
Выясним причину, по которой к объекту ВыборкаИзРезультатаЗапроса нельзя обратиться по индексу вида Выборка[0]. В платформе 1С выборка представляет собой однонаправленный курсор (итератор). До вызова метода Следующий() указатель находится перед первой записью, и прямого доступа к колонкам еще нет. Попытка обратиться к свойствам выборки без позиционирования приведет к ошибке времени выполнения.
У выборки принципиально отсутствует интерфейс прямого индексного доступа, поэтому переход к записи всегда требует вызова специального метода позиционирования.
Рассмотрим вариант, предложенный участниками обсуждения. Метод Следующий() не только сдвигает указатель на следующую строку, но и возвращает булево значение: Истина, если строка успешно прочитана, и Ложь, если записи закончились или результат запроса пуст.
Посмотрим на пример реализации в одну строку с помощью тернарного оператора:
Выборка = РезультатЗапроса.Выбрать();
Значение = ?(Выборка.Следующий(), Выборка.Принтер, "");
В этом выражении метод Выборка.Следующий() выполняется в момент проверки условия. Если запись существует, курсор перемещается на первую позицию, условие принимает значение Истина, и возвращается значение поля. Если записей нет, возвращается пустая строка или значение по умолчанию.
Важное замечание: согласно стандартам разработки фирмы «1С», использование методов с побочными эффектами (каковым является Следующий(), меняющий состояние курсора) внутри тернарного оператора ?(...) не рекомендуется. Это затрудняет отладку кода, так как в тернарное выражение нельзя поставить точку останова для проверки промежуточного состояния выборки.
Проанализируем классическую конструкцию, которая считается стандартом надежного и читаемого кода:
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
Значение = Выборка.Принтер;
Иначе
Значение = "";
КонецЕсли;
Разберем преимущества такого подхода перед компактными однострочниками:
Иначе можно не просто присвоить пустое значение, но и зафиксировать ошибку в журнале регистрации или выбросить исключение, если запись обязана присутствовать.Посмотрим на альтернативный вариант, упомянутый на форуме, где используется метод Выгрузить():
Таблица = РезультатЗапроса.Выгрузить();
Значение = ?(Таблица.Количество() > 0, Таблица[0].Принтер, "");
Либо проверка через метод Пустой() самого результата:
Значение = ?(НЕ РезультатЗапроса.Пустой(), РезультатЗапроса.Выгрузить()[0].Принтер, "");
Разберем, почему данный способ является наименее предпочтительным:
Выгрузить() физически создает в оперативной памяти объект ТаблицаЗначений, инициализирует структуру ее колонок, создает строки и копирует данные.Если мы заранее знаем, что нам нужна только первая или единственная запись, правилом хорошего тона является явное ограничение выборки в тексте запроса с помощью конструкции ПЕРВЫЕ 1:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| яПринтерЭтикеток.Принтер КАК Принтер
|ИЗ
| РегистрСведений.яПринтерЭтикеток КАК яПринтерЭтикеток
|ГДЕ
| яПринтерЭтикеток.Пользователь = &Пользователь";
Запрос.УстановитьПараметр("Пользователь", ТекущийПользователь);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Значение = ?(Выборка.Следующий(), Выборка.Принтер, Неопределено);
Конструкция ПЕРВЫЕ 1 транслируется на уровне базы данных в инструкции TOP 1 (в MS SQL Server) или LIMIT 1 (в PostgreSQL). Это указывает СУБД прекратить поиск и сканирование индексов сразу после нахождения первого совпадения, сокращая нагрузку на дисковую подсистему и сетевой трафик между сервером СУБД и сервером приложений «1С:Предприятие».
В исходной задаче данные извлекаются из регистра сведений по конкретному значению измерения Пользователь. Для непериодических регистров сведений платформа предоставляет встроенный метод менеджера, который позволяет полностью обойтись без написания текста запроса:
Отбор = Новый Структура("Пользователь", ТекущийПользователь);
ЗначенияРесурсов = РегистрыСведений.яПринтерЭтикеток.Получить(Отбор);
Значение = ЗначенияРесурсов.Принтер;
Разберем по шагам особенности этого метода:
Получить() возвращает структуру, ключами которой являются имена ресурсов регистра.СрезПоследних().Такой подход делает код лаконичным, избавляет от необходимости парсинга запроса платформой и гарантирует безопасное получение значений ресурсов.