Разработка на платформе 1С:Предприятие часто преподносит сюрпризы, которые на первый взгляд кажутся мистическими. Одной из таких ситуаций является «исчезновение» строк при обходе результата запроса. Программист видит в консоли запросов или при выгрузке в таблицу значений несколько строк, но стандартный цикл Пока Выборка.Следующий() Цикл обрабатывает их не полностью или вовсе перескакивает через одну. В этой статье мы подробно разберем, почему это происходит и как инструменты отладки могут сыграть с нами злую шутку.
Рассмотрим типичную ситуацию, описанную в практике многих разработчиков. У нас есть стандартный фрагмент кода для получения данных из документа «Увольнение сотрудника»:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДокументУвольнение.Сотрудник КАК Сотрудник
|ИЗ
| Документ.УвольнениеСотрудника КАК ДокументУвольнение
|ГДЕ
| НАЧАЛОПЕРИОДА(ДокументУвольнение.Дата, ДЕНЬ) = &Дата";
Запрос.УстановитьПараметр("Дата", ТекущаяДата());
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Здесь должна быть обработка каждой строки
Сообщить("Обработка сотрудника: " + ВыборкаДетальныеЗаписи.Сотрудник);
КонецЦикла;
Проблема заключается в следующем: при выполнении запроса мы точно знаем (и видим через РезультатЗапроса.Выгрузить().Количество()), что в выборке две строки. Однако при пошаговой отладке цикл срабатывает только один раз, причем сразу на второй записи. Если же код выполняется без точек останова, он может отрабатывать корректно. Выясним причину этого странного поведения.
Главным виновником ситуации является метод Следующий() (или Next() в англоязычном варианте). Проанализируем его механику. Данный метод выполняет два действия одновременно:
Истина или Ложь).Внимание: Если вы добавили выражение ВыборкаДетальныеЗаписи.Следующий() в окно «Табло» (Watch) или вызвали его через «Вычислить выражение» (Shift+F9), отладчик выполнит этот метод, чтобы показать вам результат. В этот самый момент курсор в оперативной памяти сервера 1С сдвинется вперед.
Представьте процесс: вы остановились на строке ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();. В этот момент указатель стоит перед первой строкой. В «Табло» у вас висит выражение ВыборкаДетальныеЗаписи.Следующий(). Платформа обновляет значения в «Табло» и — бац! — курсор уже переместился на первую строку. Когда вы нажимаете F10 и доходите до реального кода Пока ВыборкаДетальныеЗаписи.Следующий() Цикл, метод вызывается повторно, перемещая вас уже на вторую строку. Первая строка оказывается безвозвратно пропущенной для логики вашей программы.
Часто разработчики замечают, что при замене выборки на таблицу значений проблема исчезает. Давайте разберем по шагам, почему это происходит:
// Вариант с Таблицей Значений - безопасен для отладки
ТЗ = РезультатЗапроса.Выгрузить();
Для Каждого СтрокаТЗ Из ТЗ Цикл
Сообщить(СтрокаТЗ.Сотрудник);
КонецЦикла;
Проанализируем ситуацию: ТаблицаЗначений — это статичная структура данных в памяти. Когда вы перебираете её циклом Для Каждого, платформа обращается к элементам коллекции по индексу. Просмотр свойств СтрокаТЗ в отладчике не меняет индекс текущей итерации цикла. В отличие от них, ВыборкаИзРезультатаЗапроса — это динамический курсор. Она не хранит все данные в объекте сразу, а «подтягивает» их по мере движения. Любое обращение к методу движения (Следующий, СледующийПоГруппировке) меняет внутреннее состояние объекта.
Чтобы не попадать в эту ловушку, рекомендуем придерживаться следующих правил профессиональной разработки в среде 1С:
Выборка.Следующий() добавьте конкретные поля: Выборка.Сотрудник, Выборка.Дата.Выборка.Количество() > 0. Этот метод возвращает число записей, не сдвигая указатель.Следующий(), не нажимайте кнопку «Добавить в табло».Если в процессе отладки вы поняли, что случайно «прощелкали» несколько строк в Табло, не обязательно перезапускать весь процесс. Рассмотрим использование метода Сбросить().
Вы можете нажать Shift+F9 и выполнить команду:
ВыборкаДетальныеЗаписи.Сбросить();
Этот метод вернет указатель выборки в исходное состояние — в позицию «перед первой строкой». После этого ваш цикл Пока ... Следующий() снова сможет корректно обработать весь набор данных с самого начала.
Хотя в 99% случаев проблема кроется именно в отладчике, стоит кратко рассмотреть и другие технические аспекты, которые могут приводить к похожим симптомам:
ИТОГИ, но вы используете простой обход По детальным записям, выборка может вести себя не так, как вы ожидаете. Для итогов всегда нужно указывать тип обхода: РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам).Подводя итог, можно сказать: Выборка — это эффективный инструмент для работы с большими объемами данных, так как она не загружает всё в память сразу. Но эта эффективность требует от программиста дисциплины при использовании инструментов отладки. Помните, что отладчик в 1С — это полноценный участник процесса выполнения кода, и его действия имеют последствия для состояния объектов в памяти.