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