Разработка и Конфигурация · 10 мин
Как перебрать элементы справочника в 1С
Как перебрать элементы справочника 1С: выборка, запрос, перебор по иерархии. Примеры кода на встроенном языке, типичные ошибки и советы по производительности.
Перебор элементов справочника — одна из самых частых задач при разработке и доработке конфигураций 1С. Она возникает при заполнении документов, синхронизации данных, формировании отчётов и пакетной обработке. Встроенный язык предлагает несколько способов обхода: через выборку, через запрос и через прямой перебор ссылок, и у каждого есть свои условия применения.
Главное различие между способами — скорость и объём загружаемых данных. Выборка через метод Выбрать() подходит для простых сценариев, запрос — для сложных отборов и соединений, а перебор через ПолучитьОбъект() нужен только тогда, когда элемент требуется изменить. Неправильный выбор способа — частая причина медленной работы обработок.
Ниже разобраны основные варианты перебора с примерами кода, особенности работы с иерархическими справочниками, отбором и типичные ошибки, которые приводят к лишним обращениям к базе данных.
Способ 1. Перебор через Выбрать() — базовый вариант
Метод Выбрать() менеджера справочника возвращает выборку — последовательный курсор по элементам. Это самый простой способ, который подходит для обхода всех элементов или элементов по владельцу и родителю.
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Наименование + " — " + Выборка.Код);
КонецЦикла;
Выборка содержит основные реквизиты элемента без вызова ПолучитьОбъект(), поэтому работает относительно быстро. Однако табличные части элемента через выборку недоступны — для них потребуется получить объект.
Метод принимает параметры: Родитель (для иерархических справочников), Владелец (для подчинённых) и Отбор — структуру с условиями по реквизитам. Важно: отбор через параметр выполняется на стороне СУБД, а фильтрация уже внутри цикла — в памяти, что медленнее.
Отбор = Новый Структура("ПометкаУдаления", Ложь);
Выборка = Справочники.Контрагенты.Выбрать(,, Отбор);
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Ссылка);
КонецЦикла;Способ 2. Перебор через запрос — для сложных отборов
Запрос — предпочтительный способ, когда нужен отбор по нескольким условиям, соединение с другими таблицами, сортировка или группировка. Весь анализ выполняется на сервере СУБД, в 1С возвращается только готовый результат.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления
|УПОРЯДОЧИТЬ ПО
| Наименование";
Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() Цикл
Сообщить(Результат.Наименование);
КонецЦикла;
У запроса есть и альтернативный вариант обхода — через Выгрузить() в таблицу значений с последующим циклом Для Каждого. Это удобно, если данные нужны многократно или требуется индексированный поиск по колонке.
Для иерархического обхода запрос позволяет использовать конструкцию ИТОГИ ПО и обход результата по группировкам — это единственный удобный способ получить дерево элементов одним обращением к базе.
Сравнение способов перебора
Простые сценарии, обход по родителю или владельцу. Ограничение: слабые возможности отбора, нет соединений.
Сложные отборы, сортировка, соединения, большие объёмы. Требует знания языка запросов.
Нужен многократный доступ к данным или поиск по таблице. Расходует память на всю таблицу целиком.
Только когда элемент нужно изменить и записать. Самый медленный вариант — не использовать для чтения.
Способ 3. Перебор с изменением элементов
Если задача — не просто прочитать, а изменить элементы (например, заполнить реквизит у всех позиций), внутри цикла вызывается ПолучитьОбъект(), затем объект изменяется и записывается.
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
Объект = Выборка.ПолучитьОбъект();
Объект.Комментарий = "Обработано";
Объект.Записать();
КонецЦикла;
Каждый вызов Записать() — отдельное обращение к базе, поэтому на больших объёмах такая обработка может занять значительное время. Для массовых изменений оборачивайте цикл в транзакцию разумного размера или используйте паузы, чтобы не блокировать работу пользователей.
Перебор иерархического справочника
Иерархические справочники (например, «Номенклатура» с группами) требуют отдельного подхода. При вызове Выбрать() без параметров обходятся и группы, и элементы; различить их можно по свойству ЭтоГруппа.
Чтобы обойти элементы конкретной группы с учётом вложенности, в запросе используется условие Ссылка В ИЕРАРХИИ(&Группа). Это стандартный и быстрый способ выбрать всё поддерево.
Рекурсивный обход дерева
Если нужен именно рекурсивный обход с обработкой каждого уровня, используется процедура, вызывающая сама себя для дочерних элементов:
Процедура ОбойтиУровень(Родитель)
Выборка = Справочники.Номенклатура.Выбрать(Родитель);
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Наименование);
Если Выборка.ЭтоГруппа Тогда
ОбойтиУровень(Выборка.Ссылка);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Начальный вызов — ОбойтиУровень(Справочники.Номенклатура.ПустаяСсылка()). Учтите, что рекурсия делает отдельный запрос на каждый уровень каждой группы, поэтому на глубоких иерархиях запрос с ИТОГИ ПО ... ИЕРАРХИЯ работает заметно быстрее.
Отбор и фильтрация при переборе
Правило производительности простое: чем раньше применён фильтр, тем быстрее работает перебор. Иерархия условий по эффективности выглядит так: условие в запросе → параметр отбора в Выбрать() → проверка внутри цикла.
Отбор в Выбрать() поддерживает только равенство по реквизитам, поэтому условия вида «содержит подстроку» или «больше значения» доступны только в запросе. Пометку удаления почти всегда стоит исключать явно, иначе в обработку попадут неактуальные элементы.
Как выбрать способ
Типичные ошибки и производительность
Самая распространённая ошибка — вызов ПолучитьОбъект() там, где достаточно данных выборки. Каждый такой вызов загружает объект целиком, включая табличные части, и на тысячах элементов разница во времени становится кратной.
Вторая частая проблема — обращение к реквизитам через точку от ссылки внутри цикла в клиент-серверном варианте: каждое обращение может порождать отдельный запрос. Выбирайте все нужные поля сразу в запросе.
Проверка результата
После написания обхода проверьте три вещи: количество обработанных элементов (счётчик в цикле против ожидаемого), корректность отбора (не попали ли помеченные на удаление или чужие группы) и время выполнения на реальном объёме данных.
Для массовых изменений полезно сначала запустить обработку в режиме «только отчёт»: вместо записи выводить список элементов, которые будут изменены. Это позволяет убедиться в правильности логики до фактического изменения данных.
Как проверить результат
Совпадает с числом элементов по тому же отбору в списке справочника
Счётчик меньше или больше ожидаемого — ошибка в отборе
Нет помеченных на удаление и элементов чужих групп
В обработку попали лишние элементы
Секунды или минуты на типовом объёме
Обработка работает часами или блокирует пользователей
Частые вопросы
Как перебрать только группы справочника, без элементов?
Используйте выборку или запрос с условием ЭтоГруппа = Истина. В запросе: ГДЕ Справочник.ЭтоГруппа. При переборе через Выбрать() проверяйте свойство выборки ЭтоГруппа внутри цикла.
Как перебрать элементы подчинённого справочника по владельцу?
Передайте владельца вторым параметром: Справочники.ЕдиницыИзмерения.Выбрать(, СсылкаНаНоменклатуру). В запросе используйте условие ГДЕ Таблица.Владелец = &Владелец.
Что быстрее: Выбрать() или запрос?
Для простого обхода без условий разница невелика. Как только появляются отборы, сортировка или соединения, запрос выигрывает, потому что вся фильтрация выполняется на сервере СУБД, а в 1С приходит только нужный результат.
Как перебрать элементы в управляемых формах на клиенте?
Напрямую перебор выполняется только на сервере. На клиенте вызывайте серверную процедуру (с директивой &НаСервере), которая выполнит обход и вернёт результат, например массив структур или таблицу значений.
Как прервать перебор досрочно?
Используйте оператор Прервать внутри цикла при выполнении условия — например, когда нужный элемент найден. Для пропуска текущей итерации без выхода из цикла применяется Продолжить.
Что важно запомнить
Для чтения используйте Выбрать() или запрос, для изменения — ПолучитьОбъект() с записью, для иерархии — условие В ИЕРАРХИИ. Фильтруйте данные как можно раньше, исключайте помеченные на удаление и тестируйте массовые обработки на копии базы.