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

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

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

Способы обхода элементов справочника 1С: Выбрать(), запрос с обходом, иерархические справочники, обход с отбором. Примеры кода на встроенном языке и типичные ошибки.

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

Чаще всего применяют два подхода: метод Выбрать() менеджера справочника и запрос с последующим обходом выборки. Первый проще в написании, второй быстрее на больших объёмах и гибче, когда нужны дополнительные поля или соединения с другими таблицами. Отдельный случай — иерархические справочники, где порядок обхода и выбор родителя имеют значение.

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

Обход через метод Выбрать(): базовый вариант

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

Выборка = Справочники.Номенклатура.Выбрать();

Пока Выборка.Следующий() Цикл

Сообщить(Выборка.Ссылка);

КонецЦикла;

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

Метод Выбрать() без параметров возвращает все элементы, включая помеченные на удаление. Если помеченные объекты не нужны, их следует отфильтровать проверкой Выборка.ПометкаУдаления или использовать запрос с условием.

Обход через запрос: быстрее и гибче

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

Запрос = Новый Запрос;

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

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Ссылка,

| Номенклатура.Наименование КАК Наименование

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ГДЕ

| НЕ Номенклатура.ПометкаУдаления";

Результат = Запрос.Выполнить();

Выборка = Результат.Выбрать();

Пока Выборка.Следующий() Цикл

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

КонецЦикла;

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

Если результат нужен один раз и весь целиком, вместо выборки можно выгрузить результат в таблицу значений: Таблица = Результат.Выгрузить(), затем обойти её циклом Для Каждого Строка Из Таблица Цикл. Такой вариант удобен, когда данные нужно обходить несколько раз или сортировать произвольно, но требует больше памяти.

Обход с отбором по реквизитам и родителю

У метода Выбрать() есть параметры: родитель, владелец, отбор и порядок. Например, обход элементов только одной группы:

Выборка = Справочники.Номенклатура.Выбрать(ГруппаТоваров);

Пока Выборка.Следующий() Цикл

// элементы группы ГруппаТоваров

КонецЦикла;

Для отбора по произвольному реквизиту используется структура отбора:

Отбор = Новый Структура("Артикул", "Т-001");

Выборка = Справочники.Номенклатура.Выбрать(,, Отбор);

Ограничение этого способа: отбор через структуру поддерживает только равенство и только по индексированным реквизитам работает быстро. Для сложных условий (неравенство, подобие, несколько условий «ИЛИ») правильнее использовать запрос с конструкцией ГДЕ.

Сравнение вариантов обхода

Выбрать() без параметров

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

Выбрать() с отбором

Хорош для отбора по родителю, владельцу или равенству реквизита. Не поддерживает сложные условия.

Запрос с выборкой

Оптимален для больших объёмов, сложных условий и соединений. Требует аккуратного написания текста запроса.

Запрос с выгрузкой в таблицу

Удобен при многократном обходе и сортировке результата. Расходует больше памяти на больших выборках.

Обход иерархического справочника

В иерархических справочниках простой обход Выбрать() вернёт все элементы и группы вперемешку. Чтобы различать их, проверяйте свойство Выборка.ЭтоГруппа (для справочников с иерархией групп и элементов). Если нужны только «товарные» позиции без папок, добавьте в запрос условие НЕ Номенклатура.ЭтоГруппа.

Для обхода с сохранением структуры дерева в запросе укажите итоги по иерархии:

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

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Ссылка

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ИТОГИ ПО

| Номенклатура.Ссылка ТОЛЬКО ИЕРАРХИЯ";

Выборка = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

Такой обход возвращает элементы в порядке дерева: сначала группа, затем вложенные в неё элементы. Это используется при построении отчётов по структуре каталога или при выгрузке иерархии во внешние системы.

Изменение элементов во время обхода

Частая задача — не просто прочитать элементы, а изменить их: заполнить реквизит, установить пометку удаления, пересчитать значение. В этом случае по ссылке из выборки получают объект и записывают его:

Выборка = Справочники.Номенклатура.Выбрать();

Пока Выборка.Следующий() Цикл

Объект = Выборка.Ссылка.ПолучитьОбъект();

Объект.Комментарий = "Обработано";

Объект.Записать();

КонецЦикла;

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

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

Типичные ошибки и как проверить результат

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

Проверить полноту обхода просто: посчитайте количество итераций и сравните с количеством элементов, которое возвращает запрос ВЫБРАТЬ КОЛИЧЕСТВО(*) по тому же условию. Совпадение чисел — признак того, что обошли всё, что планировали.

Типичные ошибки
01
Чтение реквизитов через выборку Выбрать() в цикле — многократные обращения к базе и медленная работа на больших справочниках.
02
Отсутствие фильтра по ПометкаУдаления — в обработку попадают помеченные на удаление элементы.
03
Использование ПолучитьОбъект() там, где достаточно ссылки — лишний расход памяти и времени.
04
Запись объектов без обработки исключений — один проблемный элемент прерывает всю обработку.
05
Сравнение ссылок строкой вместо сравнения ссылок — ложные результаты отбора.
Контрольные точки

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

Количество обработанных элементов
Норма

Совпадает с КОЛИЧЕСТВО(*) по тому же условию в запросе

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

Число меньше — часть элементов пропущена или отфильтрована неявно

Помеченные на удаление
Норма

В обработку попадают только нужные элементы по заданному условию

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

В результате присутствуют помеченные объекты, которых быть не должно

Время выполнения
Норма

Обход идёт равномерно, без резких пауз на каждом элементе

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

Каждая итерация занимает заметное время — вероятно, лишние чтения из базы

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

Чем обход через запрос лучше обхода через Выбрать()?

Запрос выполняется на сервере СУБД один раз и сразу возвращает нужные поля, тогда как выборка Выбрать() при обращении к реквизитам дочитывает объекты по одному. На справочниках в десятки тысяч элементов разница в скорости заметна. Кроме того, запрос позволяет задать сложные условия, сортировку и соединения с другими таблицами.

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

В запросе добавьте условие «НЕ Справочник.ЭтоГруппа» — для справочников с видом иерархии «группы и элементы». При обходе через Выбрать() проверяйте свойство Выборка.ЭтоГруппа внутри цикла и пропускайте группы оператором Продолжить.

Почему в цикле попадаются помеченные на удаление элементы?

Метод Выбрать() по умолчанию возвращает все элементы, включая помеченные. Добавьте внутри цикла проверку «Если Выборка.ПометкаУдаления Тогда Продолжить» либо используйте запрос с условием «НЕ ПометкаУдаления», что также сократит объём выборки.

Можно ли изменять элементы прямо внутри цикла обхода?

Да, через ПолучитьОбъект() и Записать(). Но учитывайте, что запись вызывает обработчики и проверки заполнения, поэтому оборачивайте её в Попытка/Исключение и фиксируйте ошибки. Для больших объёмов выполняйте обработку в фоновом задании и предварительно делайте копию базы.

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

У метода Выбрать() четвёртым параметром передаётся строка порядка, например "Наименование". В запросе используйте конструкцию УПОРЯДОЧИТЬ ПО Наименование. Вариант с запросом предпочтительнее, так как сортировка выполняется на стороне СУБД.

Что делать, если элементов миллионы и обход занимает часы?

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

Итог

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

Для простого обхода достаточно Выбрать() с циклом Пока Выборка.Следующий(). Для больших объёмов, отборов и иерархии используйте запрос. Всегда фильтруйте помеченные на удаление, избегайте лишних чтений объектов в цикле, а перед массовой записью делайте резервную копию базы.