Почему нельзя использовать конструкцию Запрос.Выполнить().Выбрать().Следующий() в условии цикла?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике программирования на платформе 1С:Предприятие 8 начинающие разработчики иногда пытаются сократить код, объединяя выполнение запроса, создание выборки и переход к следующей записи в одну строку внутри условия цикла Пока. Рассмотрим, к чему приводит использование конструкции Пока Запрос.Выполнить().Выбрать().Следующий() Цикл ... КонецЦикла и почему этот код считается грубой ошибкой.

Разберем механизм работы цикла и объекта Запрос

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

Рассмотрим пошагово, что происходит в коде Пока Запрос.Выполнить().Выбрать().Следующий() Цикл:

  1. Вызов метода Выполнить(): На каждой итерации платформа заново обращается к серверу баз данных, выполняет текст запроса и получает новый объект РезультатЗапроса.
  2. Вызов метода Выбрать(): Из только что полученного результата запроса создается абсолютно новый объект ВыборкаИзРезультатаЗапроса.
  3. Метод Следующий(): У любого нового объекта выборки курсор (указатель на текущую строку) по умолчанию установлен в позицию перед первой записью. При первом вызове Следующий() курсор перемещается на самую первую строку результата. Если в результате запроса есть хотя бы одна запись, метод возвращает Истина.
  4. Итерация цикла: Тело цикла выполняется, и управление снова возвращается к заголовку Пока.
  5. Повторение ошибки: На второй итерации система снова выполняет запрос, снова создает новую выборку и снова вызывает Следующий(). Поскольку выборка новая, она опять встает на первую строку.

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

Последствия для производительности и системы

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

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

Для корректной работы необходимо разделять этапы подготовки данных и их обхода. Рассмотрим стандартный и правильный алгоритм работы с объектом Запрос:


// 1. Подготавливаем результат один раз
РезультатЗапроса = Запрос.Выполнить();

// 2. Создаем объект выборки один раз
Выборка = РезультатЗапроса.Выбрать();

// 3. Используем цикл для перемещения курсора внутри существующей выборки
Пока Выборка.Следующий() Цикл
    // Здесь мы последовательно переходим от 1-й строки ко 2-й, 3-й и так далее
    Счетчик = Счетчик + 1;
КонецЦикла;

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

Эффективные способы подсчета количества строк

Если ваша задача заключается исключительно в подсчете количества записей (как в примере автора темы), использование цикла — не самый оптимальный путь. Рассмотрим альтернативы:

  1. Подсчет на стороне сервера БД: Это самый быстрый способ. Измените текст запроса, чтобы он возвращал уже готовое число.
    
    Запрос.Текст = "ВЫБРАТЬ КОЛИЧЕСТВО(*) ИЗ Справочник.Номенклатура";
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        ОбщееКоличество = Выборка[0];
    КонецЕсли;
    
  2. Использование метода Количество(): Если вам нужен и сам результат, и знание о числе строк, используйте встроенный метод объекта выборки или результата.
    
    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();
    ОбщееКоличество = Выборка.Количество(); // Возвращает число строк без обхода в цикле
    
  3. Выгрузка в таблицу значений: Метод подходит, если данные все равно нужно передать в таблицу для дальнейшей обработки.
    
    ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
    КоличествоСтрок = ТаблицаРезультата.Количество();
    

Контекст Системы компоновки данных (СКД)

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

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

← На главную