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