К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как обработать результат запроса в 1С

Как обработать результат запроса в 1С: методы Выполнить, Выбрать, Выгрузить, обход группировок, проверка на пустоту, работа с таблицей значений и типичные ошибки.

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

Типовая цепочка выглядит так: создаётся объект запроса, задаётся текст, устанавливаются параметры, вызывается Выполнить(), а затем результат обходится построчно или выгружается целиком. Ошибки чаще всего возникают не в самом запросе, а на этапе обхода: разработчик не проверяет результат на пустоту, неверно обрабатывает иерархические группировки или грузит в память миллионы строк, когда достаточно последовательного чтения.

Базовая последовательность: от текста запроса до результата

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

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Товар,

| Номенклатура.Артикул КАК Артикул

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ГДЕ

| Номенклатура.Родитель = &Группа";

Запрос.УстановитьПараметр("Группа", ВыбраннаяГруппа);

РезультатЗапроса = Запрос.Выполнить();

Важно понимать: до вызова Выполнить() никаких обращений к базе данных не происходит. Само выполнение может занять заметное время на больших таблицах, поэтому в клиент-серверном режиме тяжёлые запросы выполняют в серверных процедурах, а не на клиенте. Результат запроса — это отдельный объект, и именно с ним ведётся вся дальнейшая работа.

Порядок действий
  1. 1
    Создайте объект Запрос и задайте текст запроса
  2. 2
    Установите все параметры через УстановитьПараметр()
  3. 3
    Выполните запрос и сохраните результат в переменную
  4. 4
    Проверьте результат методом Пустой()
  5. 5
    Обойдите данные через Выбрать() или выгрузите через Выгрузить()

Проверка результата на пустоту перед обходом

Метод Пустой() возвращает Истина, если запрос не вернул ни одной строки. Эта проверка позволяет избежать лишней работы и корректно обработать ситуацию «данных нет»: вывести сообщение пользователю, записать предупреждение в журнал или прервать алгоритм. Без проверки код молча пройдёт цикл ноль раз, и причину отсутствия результата придётся искать дольше.

Если РезультатЗапроса.Пустой() Тогда

Сообщить("По заданным условиям данные не найдены.");

Возврат;

КонецЕсли;

Отдельный случай — запросы с итогами. Если в тексте есть секция ИТОГИ, результат может быть формально непустым (содержит общую итоговую строку), даже когда детальных записей нет. В таких ситуациях проверку делают на уровне выборки: выполняют Следующий() и анализируют тип записи через ТипЗаписи().

Обход результата через Выбрать()

Метод Выбрать() возвращает выборку — объект для последовательного построчного чтения. Классический цикл выглядит так: Пока Выборка.Следующий() Цикл ... КонецЦикла;. На каждой итерации поля текущей строки доступны по именам через точку: Выборка.Товар, Выборка.Артикул. Имена соответствуют псевдонимам полей, заданным в тексте запроса.

Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл

Сообщить(Строка(Выборка.Товар) + " — " + Выборка.Артикул);

КонецЦикла;

Главное преимущество выборки — экономия памяти: данные читаются порциями по мере обхода, а не загружаются целиком. Это критично при обработке сотен тысяч и миллионов строк. Ограничение тоже существенное: выборка однонаправленная, вернуться к предыдущей строке нельзя, а количество записей заранее неизвестно (метод Количество() у выборки есть, но на практике его поведение зависит от способа обхода и не всегда информативно до полного обхода).

Обход иерархических данных и группировок

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

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

Пока ВыборкаГрупп.Следующий() Цикл

Сообщить("Группа: " + ВыборкаГрупп.Склад);

Детали = ВыборкаГрупп.Выбрать();

Пока Детали.Следующий() Цикл

Сообщить(" " + Детали.Товар + ": " + Детали.Количество);

КонецЦикла;

КонецЦикла;

Типичные ошибки
01
Обход иерархического запроса без указания ОбходРезультатаЗапроса.ПоГруппировкам — строки перемешиваются, итоги попадают в общий поток
02
Обращение к полю выборки, которого нет в тексте запроса — ошибка исполнения; проверяйте псевдонимы полей
03
Использование Выгрузить() для миллионов строк — превышение памяти и падение сеанса
04
Повторное выполнение запроса внутри цикла обхода вместо вложенной выборки — резкое падение производительности

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

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

ТаблицаТоваров = РезультатЗапроса.Выгрузить();

Для Каждого СтрокаТовара Из ТаблицаТоваров Цикл

// обработка строки

КонецЦикла;

// Поиск по таблице без повторного запроса

НайденнаяСтрока = ТаблицаТоваров.Найти(ИскомыйАртикул, "Артикул");

Если НайденнаяСтрока <> Неопределено Тогда

// строка найдена

КонецЕсли;

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

Когда выгрузка опасна

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

Сравнение способов обработки результата

КритерийВыбрать() (выборка)Выгрузить() (таблица значений)
Расход памятиМинимальный, чтение порциямиВесь результат в памяти
Направление обходаТолько вперёдПроизвольный доступ по индексу
Повторный обходНевозможен без нового запросаСколько угодно раз
Поиск по даннымНедоступенНайти(), НайтиСтроки()
ИерархияВложенные выборки по группировкамДерево значений
Типовой сценарийПроведение, пакетная обработкаЗаполнение табличных частей, форм
Маршрут решения

Как выбрать способ обработки

Строки обрабатываются один раз последовательно, объём большойИспользуйте Выбрать() и цикл Пока Следующий()
Нужен многократный обход, поиск или сортировка в памятиИспользуйте Выгрузить() в таблицу значений
Запрос содержит ИТОГИ и нужна структура группВыбрать(ПоГруппировкам) с вложенными выборками или выгрузка в дерево
Результат нужен на клиенте, а запрос выполняется на сервереВыгрузите в таблицу значений и передайте её, выборку между контекстами не передают

Проверка корректности обработки

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

В-третьих, при обходе иерархии убедитесь, что итоговые строки не попали в детальную обработку — признак ошибки: в документах или сообщениях появляются строки с наименованиями групп вместо элементов. Для отладки удобно временно выводить Выборка.ТипЗаписи() и уровень группировки.

Контрольные точки

Как проверить результат

Количество обработанных строк
Норма

Совпадает с числом строк в консоли запросов

Тревожный признак

Меньше ожидаемого — фильтр отсек данные или оборван цикл

Значения полей из соединённых таблиц
Норма

Заполнены или явно обработан NULL

Тревожный признак

Ошибки приведения типов, пустые значения там, где ожидались данные

Иерархический обход
Норма

Группы и детали разделены по уровням

Тревожный признак

Итоговые строки попадают в детальную обработку

Потребление памяти при выгрузке
Норма

Таблица выгружается быстро, сеанс стабилен

Тревожный признак

Долгая выгрузка, рост памяти, аварийное завершение

Типичные ошибки и ограничения

Одна из самых частых проблем — попытка использовать выборку после завершения транзакции или передачи между серверными вызовами. Выборка привязана к контексту выполнения; передавать между клиентом и сервером можно только сериализуемые данные — таблицу значений или массивы структур. Вторая распространённая ошибка — модификация данных внутри цикла обхода с одновременным выполнением новых запросов к тем же таблицам без учёта блокировок, что в многопользовательском режиме приводит к конфликтам.

Также учитывайте ограничение на типы полей: поля типа «ХранилищеЗначения» из результата запроса нужно извлекать через Получить(), а поля с составным типом требуют проверки типа значения перед использованием. Если запрос возвращает ссылочные поля, помните, что обращение к реквизитам ссылки через точку («запрос через точку») порождает скрытые обращения к базе — для массовой обработки такие реквизиты лучше выбирать явно в тексте запроса.

Если внутри цикла обхода выборки нужно записывать объекты, разумно фиксировать транзакцию порциями (например, каждые несколько сотен строк): при ошибке не потеряется вся обработка, а блокировки не будут удерживаться слишком долго.
ПЗ
Практическое замечание

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

Чем отличается РезультатЗапроса от Выборки?

Результат запроса — это объект, возвращаемый методом Выполнить(); он хранит данные и метаинформацию (колонки, пустоту). Выборка — это курсор для построчного чтения, получаемый из результата методом Выбрать(). Из одного результата можно получить выборку или выгрузить таблицу, но не наоборот.

Как узнать количество строк результата до обхода?

Для таблицы значений после Выгрузить() количество доступно через метод Количество(). Для выборки надёжного способа нет: либо выгружайте результат, либо выполняйте отдельный запрос с функцией КОЛИЧЕСТВО(), либо считайте строки в цикле обхода.

Можно ли обойти результат запроса дважды?

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

Что делать, если поля выборки содержат NULL?

NULL появляется при левых соединениях, когда в присоединённой таблице нет подходящей строки. Проверяйте значение сравнением с NULL через «Если Выборка.Поле = NULL» либо устраняйте NULL на уровне запроса функцией ЕСТЬNULL(Поле, ЗначениеПоУмолчанию).

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

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

Когда стоит использовать дерево значений вместо таблицы?

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

Итог

Что важно запомнить

Результат запроса обрабатывается через Выбрать() для экономного последовательного обхода или через Выгрузить() для работы с таблицей значений в памяти. Всегда проверяйте результат методом Пустой(), для иерархических запросов используйте обход по группировкам, а большие объёмы не выгружайте в память целиком.