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