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