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

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

Как в 1С 7.7 перебрать справочник элементов

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

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

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

Ниже разобраны оба способа с рабочими примерами кода, особенностями обхода иерархических и подчинённых справочников, а также типичные ошибки, из-за которых перебор «не видит» часть элементов или работает медленно.

Базовый перебор через ВыбратьЭлементы()

Основной инструмент — метод справочника ВыбратьЭлементы(), который открывает выборку, и метод ПолучитьЭлемент(), который переходит к следующему элементу выборки. Пока элементы не закончились, ПолучитьЭлемент() возвращает 1, что удобно использовать как условие цикла.

Процедура ПеребратьТовары()

Спр = СоздатьОбъект("Справочник.Номенклатура");

Спр.ВыбратьЭлементы();

Пока Спр.ПолучитьЭлемент() = 1 Цикл

Сообщить(Спр.Наименование);

КонецЦикла;

КонецПроцедуры

Внутри цикла доступны все реквизиты текущего элемента через точку: Спр.Код, Спр.Наименование, Спр.Цена и так далее. Сам объект Спр после ПолучитьЭлемент() «спозиционирован» на текущем элементе, поэтому обращаться к нему нужно именно как к объекту выборки, а не создавать новый объект для каждого элемента.

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

1c
Спр = СоздатьОбъект("Справочник.Контрагенты");
Спр.ПорядокНаименований();
Спр.ВыбратьЭлементы();
Пока Спр.ПолучитьЭлемент() = 1 Цикл
    Сообщить(Спр.Код + " | " + Спр.Наименование);
КонецЦикла;

Обход иерархического справочника и фильтрация групп

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

Пока Спр.ПолучитьЭлемент() = 1 Цикл

Если Спр.ЭтоГруппа() = 1 Тогда

Продолжить;

КонецЕсли;

// обработка элемента

КонецЦикла;

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

Признак ИспользоватьРодителя() сбрасывается вызовом того же метода с пустым значением. Если забыть сбросить родителя и переиспользовать объект дальше, следующая выборка молча вернёт неполный набор данных — это частая причина «пропавших» элементов.

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

Перебор подчинённого справочника

Подчинённые справочники (например, «Договоры» у «Контрагентов» или «Единицы» у «Номенклатуры») перебираются с указанием владельца методом ИспользоватьВладельца(). Без него выборка вернёт элементы всех владельцев сразу, что почти никогда не нужно.

СпрДог = СоздатьОбъект("Справочник.Договоры");

СпрДог.ИспользоватьВладельца(ТекКонтрагент);

СпрДог.ВыбратьЭлементы();

Пока СпрДог.ПолучитьЭлемент() = 1 Цикл

Сообщить(СпрДог.Наименование);

КонецЦикла;

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

Перебор через запрос

Для отбора по условиям, группировки и подсчёта итогов в 1С 7.7 применяется объект Запрос с текстом на встроенном языке запросов. Запрос выполняется быстрее цикла, особенно на больших справочниках, потому что отбор происходит на стороне источника данных, а не построчным чтением.

Запрос = СоздатьОбъект("Запрос");

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

"//{{ЗАПРОС(Сформировать)

|Тов = Справочник.Номенклатура.ТекущийЭлемент;

|Группировка Тов;

|Условие(Тов.ПометкаУдаления() = 0);

|"//}}ЗАПРОС

;

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда

Возврат;

КонецЕсли;

Пока Запрос.Группировка(1) = 1 Цикл

Сообщить(Запрос.Тов.Наименование);

КонецЦикла;

Результат запроса обходится методом Группировка(Номер) по номеру группировки. Обратите внимание на проверку возврата Выполнить(): при синтаксической ошибке в тексте запроса метод вернёт 0, и без проверки программа продолжит работу с пустым результатом.

Сравнение вариантов

ВыбратьЭлементы()

Подходит для простого обхода и изменения элементов. Медленнее на больших объёмах, отбор условий только вручную внутри цикла.

Запрос

Быстрый отбор и группировки, но результат доступен только для чтения — изменять элементы нужно через отдельный объект справочника.

ВыбратьЭлементыПоРеквизиту()

Перебор по конкретному значению отборного реквизита; работает только если у реквизита включён отбор в конфигураторе.

Изменение элементов внутри цикла

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

Пока Спр.ПолучитьЭлемент() = 1 Цикл

Если Спр.ЭтоГруппа() = 1 Тогда

Продолжить;

КонецЕсли;

Спр.СтавкаНДС = Ставка20;

Спр.Записать();

КонецЦикла;

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

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

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

Для диагностики скорости полезно замерить время выполнения через _GetPerformanceCounter() или простыми сообщениями до и после цикла. Если перебор десятков тысяч элементов занимает неоправданно много времени, проверьте: не выполняется ли внутри цикла поиск или запись лишних объектов, не открыт ли монопольный конфликт с другими пользователями, нельзя ли заменить цикл запросом.

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

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

Счётчик обработанных элементов
Норма

Совпадает с ожидаемым количеством

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

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

Значения реквизитов после записи
Норма

Новые значения видны в форме элемента

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

Значения старые — забыт вызов Записать()

Полнота выборки
Норма

Присутствуют элементы из всех нужных групп

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

Видны только элементы одной группы — не сброшен ИспользоватьРодителя()

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

Почему выборка не находит помеченные на удаление элементы?

Выборка по умолчанию включает помеченные на удаление элементы — они перебираются наравне с обычными. Если они вам не нужны, фильтруйте их внутри цикла проверкой Спр.ПометкаУдаления() = 1 с оператором Продолжить. Обратная ситуация — когда «пропадают» обычные элементы — чаще всего связана с установленным родителем или владельцем.

Как перебрать справочник в определённом порядке?

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

Можно ли прервать перебор досрочно?

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

Что быстрее: цикл или запрос?

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

Как перебрать только группы справочника?

Внутри цикла используйте обратную проверку: если Спр.ЭтоГруппа() = 0, то Продолжить. Альтернатива — метод ВыбратьГруппы(), но он доступен не во всех контекстах, поэтому фильтрация внутри цикла надёжнее и работает универсально.

Итог

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

Базовый перебор — ВыбратьЭлементы() плюс цикл Пока ПолучитьЭлемент()=1. Группы отсекайте через ЭтоГруппа(), владельца и родителя задавайте до выборки и не забывайте сбрасывать. Для отборов и итогов используйте Запрос, для изменений — Записать() и предварительную копию базы.