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