Извлекаем данные из результата запроса на встроенном языке

· 9 мин

Содержание

Запрос вы написали и выполнили, а дальше нужно достать из него конкретные значения. Коротко: Запрос.Выполнить() возвращает объект РезультатЗапроса, а вытаскивают из него данные двумя способами — через выборку (Выбрать) построчно или выгрузкой целиком в таблицу значений (Выгрузить). Для одного значения берут первую строку выборки, для пачки строк — перебирают цикл или работают с таблицей.

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

Что возвращает Выполнить и как к этому подступиться

Метод Запрос.Выполнить() не отдаёт значения напрямую — он возвращает объект РезультатЗапроса. Это контейнер: внутри строки и колонки, но обратиться к ним как к массиву сразу нельзя. Сначала результат нужно «раскрыть» одним из методов.

Весь путь выглядит так:

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |   Номенклатура.Ссылка КАК Ссылка,
    |   Номенклатура.Наименование КАК Наименование,
    |   Номенклатура.Артикул КАК Артикул
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |   НЕ Номенклатура.ПометкаУдаления";
Результат = Запрос.Выполнить();

Теперь у объекта Результат есть три основных метода:

  • 📊 Выбрать() — возвращает выборку ВыборкаИзРезультатаЗапроса для построчного обхода;
  • 📋 Выгрузить() — возвращает ТаблицаЗначений со всеми строками сразу;
  • ✅ Пустой() — возвращает Истина, если запрос не вернул ни одной строки.

Отдельно полезно свойство Колонки — через него видно, какие поля пришли и в каком порядке. Имя поля в выборке совпадает с псевдонимом после КАК в тексте запроса. Если псевдоним не задан для вычисляемого поля, обратиться к нему по имени не получится — платформа выдаст ошибку «Поле объекта не обнаружено».

Перебор строк выборкой: основной способ

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

Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Наименование + " — " + Выборка.Артикул);
КонецЦикла;

К полю обращаются по имени через точку: Выборка.Наименование. Это читается лучше всего. Есть и альтернативы:

  • по строковому имени — Выборка["Наименование"], удобно, когда имя поля хранится в переменной;
  • по индексу колонки — Выборка[0], где 0 — первая колонка в том порядке, в каком поля идут в запросе.

Указатель выборки стартует «перед первой строкой». Поэтому до первого Следующий() обращаться к полям нельзя — значений ещё нет. Это причина самой частой ошибки новичков (разберём ниже).

Повторно пройти ту же выборку нельзя — после цикла указатель стоит в конце. Чтобы прочитать строки заново, вызовите Результат.Выбрать() ещё раз: каждый вызов создаёт свежую выборку с указателем в начале.

Количество строк заранее можно узнать методом Выборка.Количество() — он не двигает указатель.

Режимы обхода: прямой, по группировкам, с иерархией

У метода Выбрать() есть параметр — тип обхода ОбходРезультатаЗапроса:

РежимКогда применять
Прямойобычный плоский список строк (режим по умолчанию)
ПоГруппировкамв запросе есть ИТОГИ ПО — обходим по уровням группировки
ПоГруппировкамСИерархиейИТОГИ с иерархией, например по группам номенклатуры

Для плоского результата режим указывать не обязательно — он прямой по умолчанию:

Выборка = Результат.Выбрать(ОбходРезультатаЗапроса.Прямой);

Режимы с группировками нужны, когда в запросе есть секция ИТОГИ ПО. Тогда верхняя выборка перебирает итоговые строки, а внутри каждой берут вложенную выборку методом Выбрать() уже по детальным записям. Для простых задач «достать значение» это не требуется — хватает прямого обхода.

Как взять одно значение или только первую строку

Частый случай: запрос заведомо возвращает одну строку (например, настройку, остаток по конкретной позиции, результат агрегата). Тогда цикл не нужен — достаточно одного Следующий():

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

Такая конструкция Если Выборка.Следующий() Тогда одновременно переходит на первую строку и проверяет, что она вообще есть. Не пишите Выборка.Следующий(); без проверки — если строк нет, обращение к полю следующей строкой вызовет ошибку.

Когда нужно единственное число — количество, сумму, максимум — запрос сам сворачивает всё в одну строку агрегатной функцией. Берём её так же:

Запрос.Текст =
    "ВЫБРАТЬ
    |   КОЛИЧЕСТВО(*) КАК Кол
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |   НЕ Номенклатура.ПометкаУдаления";
Выборка = Запрос.Выполнить().Выбрать();
Выборка.Следующий();
ВсегоПозиций = Выборка.Кол;

Здесь проверку можно опустить: агрегат без группировки всегда даёт ровно одну строку, даже если считать нечего (вернётся 0 или NULL). Но привычка проверять Следующий() лишней не будет.

Если нужна именно первая строка из многих, добавьте в запрос ПЕРВЫЕ 1 и упорядочивание УПОРЯДОЧИТЬ ПО, чтобы «первая» была предсказуемой, а не случайной.

Выгрузка результата в таблицу значений

Когда с данными надо что-то делать после запроса — сортировать, искать строку, передавать в другую процедуру, выводить в табличную часть — удобнее выгрузить всё разом в таблицу значений:

ТаблицаНоменклатуры = Запрос.Выполнить().Выгрузить();
Для Каждого Строка Из ТаблицаНоменклатуры Цикл
    Сообщить(Строка.Наименование);
КонецЦикла;

Колонки таблицы называются так же, как поля запроса. Дальше доступны все методы ТаблицаЗначений:

  • ТаблицаНоменклатуры[0] — первая строка (или .Получить(0));
  • Найти(Значение, "Артикул") — поиск строки по значению колонки;
  • НайтиСтроки(Новый Структура("Артикул", "АБ-001")) — отбор нескольких строк по условию;
  • Сортировать("Наименование Возр") — упорядочить уже в памяти;
  • ВыгрузитьКолонку("Ссылка") — вытащить одну колонку в массив.

Последнее — короткий путь, когда из запроса нужен только массив ссылок:

МассивСсылок = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

Выгрузка тоже принимает тип обхода: Выгрузить(ОбходРезультатаЗапроса.Прямой). Для плоского результата параметр не нужен.

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

Несколько запросов сразу: пакетное выполнение

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

Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Склад", Склад);
Запрос.Текст =
    "ВЫБРАТЬ КОЛИЧЕСТВО(*) КАК Кол ИЗ Справочник.Номенклатура;
    |ВЫБРАТЬ Склад.Наименование КАК Имя ИЗ Справочник.Склады КАК Склад
    |ГДЕ Склад.Ссылка = &Склад";
МассивРезультатов = Запрос.ВыполнитьПакет();

Метод ВыполнитьПакет() возвращает массив: по одному объекту РезультатЗапроса на каждый запрос пакета, в порядке их следования. Дальше с каждым элементом работают как обычно:

КоличествоНоменклатуры = МассивРезультатов[0].Выбрать();
КоличествоНоменклатуры.Следующий();
Всего = КоличествоНоменклатуры.Кол;

ВыборкаСклада = МассивРезультатов[1].Выбрать();
Если ВыборкаСклада.Следующий() Тогда
    ИмяСклада = ВыборкаСклада.Имя;
КонецЕсли;

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

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

Что делать, когда запрос ничего не вернул

Пустой результат — не ошибка, а штатная ситуация: товар не нашёлся, остатков нет, подходящих документов не оказалось. Код должен это корректно пережить.

Проверка одной строкой — метод Пустой():

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

Три рабочих приёма под разные задачи:

  1. Выборка в цикле — если строк нет, тело Пока ... Цикл просто не выполнится ни разу. Отдельная проверка не обязательна.
  2. Одно значение — связка Если Выборка.Следующий() Тогда ... Иначе задаёт значение по умолчанию (пустую строку, Неопределено, 0).
  3. Явный Пустой() — когда при отсутствии данных нужно прервать процедуру или показать сообщение до начала обработки.

Отдельно про NULL: агрегат СУММА() по пустому набору строк вернёт не 0, а NULL. Чтобы получить ноль, оберните поле в запросе: ЕСТЬNULL(СУММА(Сумма), 0). Иначе арифметика с NULL в коде даст неожиданный результат.

Частые ошибки и как ускорить получение данных

Ошибки, которые встречаются чаще всего:

  • ❌ Обращение к полю до Следующий(). Получили выборку и сразу пишете Выборка.Наименование — указатель ещё перед первой строкой, значения нет. Сначала Следующий(), потом поля.
  • ❌ Следующий() без проверки для одной строки. Если результат пуст, обращение к полю оборвётся ошибкой. Всегда проверяйте возврат Следующий().
  • ❌ Поле без псевдонима КАК. Вычисляемое выражение без КАК ИмяПоля недоступно по имени — задавайте псевдоним каждому полю.
  • ❌ Повторный обход той же выборки. После цикла указатель в конце. Нужен второй проход — вызывайте Выбрать() заново.
  • ❌ Путаница Выгрузить/Выбрать на больших объёмах. Для разового перебора миллионов строк выгрузка в таблицу съедает память зря.

Что ускоряет работу с результатом:

  • 🚀 Запрос вне цикла. Если внутри цикла по документам вы для каждого выполняете новый запрос — это классический тормоз. Соберите все нужные ключи и выполните один запрос с условием В (&МассивКлючей), а результат положите в таблицу или соответствие.
  • 🚀 Соответствие для быстрого поиска. Когда по результату нужен частый поиск по ключу, выгрузите в ТаблицаЗначений, а лучше постройте Соответствие: поиск по нему мгновенный, в отличие от перебора таблицы.
  • 🚀 Только нужные поля. Не тащите в запрос лишние колонки «на всякий случай» — каждое поле это чтение с диска и память.
  • 🚀 Параметры вместо склейки текста. Значения отбора передавайте через Запрос.УстановитьПараметр("Ключ", Значение), а не вставляйте в текст запроса строкой. Так работает кэш запросов и нет проблем с типами.

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

ТаблицаЦен = Запрос.Выполнить().Выгрузить();
ЦеныПоНоменклатуре = Новый Соответствие;
Для Каждого Строка Из ТаблицаЦен Цикл
    ЦеныПоНоменклатуре.Вставить(Строка.Номенклатура, Строка.Цена);
КонецЦикла;

// дальше в любом месте — без повторного запроса и без перебора
Цена = ЦеныПоНоменклатуре.Получить(ТекущаяНоменклатура);
Если Цена = Неопределено Тогда
    Цена = 0; // позиции нет в результате
КонецЕсли;

Метод Соответствие.Получить(Ключ) вернёт Неопределено, если ключа нет, — это удобный способ отличить «значение не найдено» от настоящего нуля. Для частого поиска по результату такой подход на порядок быстрее, чем ТаблицаЗначений.Найти() в цикле.

Проверьте себя
Что вернёт `Запрос.Выполнить()`?
Можно ли обратиться к полю выборки до первого вызова Следующий()?
Чем лучше взять единственное число — количество или сумму?

Частые вопросы

Как получить значение поля по его индексу, а не по имени?
Через квадратные скобки: Выборка[0] вернёт значение первой колонки, Выборка[1] — второй. Порядок колонок совпадает с порядком полей в тексте запроса. По имени надёжнее — при изменении запроса индексы сдвигаются.
Сколько раз можно вызвать Выбрать у одного результата?
Сколько угодно. Каждый вызов Результат.Выбрать() создаёт новую выборку с указателем в начале. Это способ пройти результат повторно, не выполняя запрос заново.
Выгрузить или Выбрать — что быстрее?
Для однократного прохода по строкам быстрее и экономнее выборка: она читает данные порциями. Выгрузка оправдана, когда с результатом нужно работать как с коллекцией — искать, сортировать, передавать дальше.
Почему сумма по пустому набору строк равна NULL, а не нулю?
Так устроены агрегаты в языке запросов: СУММА() по нулю строк даёт NULL. Чтобы получить число, в запросе оборачивайте результат в ЕСТЬNULL(СУММА(Поле), 0).
Как сразу получить массив ссылок из результата?
Цепочкой Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка"). Метод ВыгрузитьКолонку вернёт массив значений одной колонки — удобно для дальнейшей обработки ссылок.