Обход результата запроса

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

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

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

Этот шаг — обязательное завершение почти любой работы с запросом. Сам по себе выполненный запрос бесполезен: данные надо куда-то деть — вывести пользователю, подсчитать сумму, сформировать движения документа, заполнить табличную часть. Поэтому связка «выполнил запрос → обошёл результат» встречается в коде постоянно, и делать её нужно уверенно. Разберём оба способа и научимся выбирать между ними осознанно, а не наугад.

Обход выборкой: Выбрать() и Следующий()

Самый распространённый способ — получить выборку методом Выбрать() и перебрать её циклом Пока ... Цикл:

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    Номенклатура.Наименование КАК Наименование,
    |    Номенклатура.Цена КАК Цена
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    Номенклатура.Цена > 0";

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

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

Разберём механику. Результат.Выбрать() возвращает объект ВыборкаИзРезультатаЗапроса, который стоит перед первой строкой. Каждый вызов Выборка.Следующий() сдвигает указатель на следующую строку и возвращает Истина, пока строки есть, и Ложь, когда они кончились. Внутри цикла к полям обращаются по имени: Выборка.Наименование, Выборка.Цена.

Здесь пригодилось ключевое слово КАК в тексте запроса. Мы написали Номенклатура.Цена КАК Цена — задали полю псевдоним, и теперь в коде поле доступно как Выборка.Цена. Если поле — это выражение (например, Цена * Количество), псевдоним обязателен: иначе к полю не обратиться по имени.

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

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

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

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

Сообщить("Найдено строк: " + Таблица.Количество());

Для Каждого Строка Из Таблица Цикл
    Сообщить(Строка.Наименование + ": " + Строка.Цена);
КонецЦикла;

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

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

Когда что выбирать

СитуацияЧто использовать
Просто пройтись по строкам один раз (посчитать, вывести, записать движения)Выбрать() + цикл
Большой объём данных, важна экономия памятиВыбрать() + цикл
Нужно передать данные в форму, отсортировать, многократно обращатьсяВыгрузить()
Нужен доступ к строкам по индексу или поиск НайтиСтроки()Выгрузить()

Простое правило: обходите один раз — берите выборку; данные нужны как объект для дальнейшей работы — выгружайте в таблицу.

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

Выборка = Результат.Выбрать();
ОбщаяСумма = 0;

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

Сообщить("Итого: " + ОбщаяСумма);

Такой аккумулятор — один из самых частых приёмов при обходе выборки. Обратите внимание: считать итог внутри обхода уместно для простых случаев, но если нужны именно агрегаты (суммы, количества по группам), правильнее попросить их у самого запроса через СГРУППИРОВАТЬ ПО и СУММА(...) — СУБД сделает это быстрее, чем ручной цикл. Обход же незаменим там, где на каждой строке нужно совершить действие: записать движение, отправить сообщение, заполнить строку табличной части.

Проверка на пустоту: Пустой()

Перед обходом почти всегда стоит спросить: а есть ли вообще данные? Для этого у результата запроса есть метод Пустой() — он возвращает Истина, если запрос не вернул ни одной строки:

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

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

Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
    // обработка строк
КонецЦикла;

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

Важно: Пустой() не «съедает» результат — после него можно спокойно вызвать Выбрать() или Выгрузить(). А вот выборку, которую вы уже прошли циклом до конца, повторно обойти нельзя: она одноразовая. Нужно пройти второй раз — либо вызовите Выбрать() заново, либо работайте с таблицей из Выгрузить().

Как это работает

Результат запроса внутри держит уже полученный из СУБД набор строк. Выбрать() создаёт над ним лёгкий курсор-итератор: он не копирует данные, а лишь двигает указатель. Поэтому обход выборкой такой экономный. Выгрузить(), наоборот, разом материализует все строки в новую ТаблицуЗначений — отдельную структуру данных, живущую независимо от запроса.

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

Частые ошибки

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

Обращаются к полю без псевдонима. Если поле в запросе — выражение (Цена * Количество) без КАК, то к нему не обратиться из кода. Всегда давайте вычисляемым полям псевдоним через КАК.

Пытаются обойти выборку дважды. После первого полного прохода выборка исчерпана. Второй Пока Выборка.Следующий() сразу вернёт Ложь. Нужен повтор — вызовите Результат.Выбрать() ещё раз или используйте Выгрузить().

Не проверяют Пустой() и падают на первой строке. Код вида Выборка.Следующий(); Значение = Выборка.Поле; без проверки рухнет, если запрос пустой. Проверяйте Результат.Пустой() прежде, чем рассчитывать на наличие данных.

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

Итоги

  • Выполнить() возвращает объект результата; данные из него берут либо выборкой (Выбрать() + цикл), либо выгрузкой в таблицу (Выгрузить()).
  • Выборку двигает Выборка.Следующий(): он же условие цикла Пока ... Цикл; к полям обращаются по имени или псевдониму (КАК).
  • Выборка экономит память и одноразова; таблица значений удобна для формы, сортировки и многократного доступа, но грузится в память целиком.
  • Проходишь один раз — бери выборку; данные нужны как объект — выгружай в таблицу.
  • Результат.Пустой() проверяет, вернул ли запрос строки; вызывайте его до обхода, чтобы не упасть на пустом результате.
Проверьте себя
1. Как правильно организовать построчный обход результата запроса в 1С?
AВыборка = Результат.Выбрать(); Пока Выборка.Следующий() Цикл ... КонецЦикла;
BПока Результат.Следующий() Цикл ... КонецЦикла;
CДля Каждого Строка Из Результат Цикл ... КонецЦикла;
DПока Результат <> Неопределено Цикл ... КонецЦикла;
2. Данные из запроса нужно вывести на управляемую форму как таблицу и позволить пользователю сортировать их. Что выбрать?
AВыбрать() и обойти циклом — форма подхватит данные автоматически
BВыгрузить() в таблицу значений — её удобно передать в форму, сортировать и многократно обходить
CНичего, результат запроса сам является таблицей значений
DОбязательно оба метода подряд: сначала Выгрузить(), потом Выбрать()