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

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

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

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

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

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

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

Перебор элементов методом Выбрать()

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

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

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

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

КонецЦикла;

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

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

Отбор = Новый Структура("ПометкаУдаления", Ложь);

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

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

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

КонецЦикла;

Перебор через запрос: когда он выгоднее

Запрос к таблице справочника — предпочтительный вариант, когда нужно только прочитать данные, применить сложную фильтрацию, соединить справочник с другими таблицами или обработать большой объём. Запрос выполняется на стороне СУБД и не тащит в память лишние поля.

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

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

"ВЫБРАТЬ

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

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

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

|ИЗ

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

|ГДЕ

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

| И НЕ Номенклатура.ЭтоГруппа";

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

Пока Результат.Следующий() Цикл

Сообщить(Результат.Наименование);

КонецЦикла;

Обратите внимание на условие НЕ Номенклатура.ЭтоГруппа. В иерархических справочниках таблица содержит и группы, и элементы; без фильтра в выборку попадут папки, что часто искажает результаты обработки. Метод Выбрать() по умолчанию тоже возвращает группы, если справочник иерархический.

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

Сравнение способов перебора

Метод Выбрать()

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

Запрос

Быстрый на больших объёмах, гибкая фильтрация и соединения. Не подходит, если нужна встроенная логика объекта при чтении.

ВыбратьИерархически()

Обходит все уровни дерева с учётом вложенности. Только для иерархических справочников с иерархией групп и элементов.

Изменение элементов в цикле: правила безопасной записи

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

Выборка = Справочники.Контрагенты.Выбрать();

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

Если Выборка.ЭтоГруппа Тогда

Продолжить;

КонецЕсли;

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

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

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

КонецЦикла;

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

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

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

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

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

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

Отступ = "";

Для Инд = 1 По Выборка.Уровень() Цикл

Отступ = Отступ + " ";

КонецЦикла;

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

КонецЦикла;

Если нужно обойти только ветку конкретной группы, передайте её ссылку первым параметром. Для обхода дерева через запрос используйте конструкцию ИТОГИ ПО ... ИЕРАРХИЯ или условие Родитель В ИЕРАРХИИ (&Группа) — второй вариант проще и обычно достаточен для выборки всех элементов поддерева.

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

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

// Все договоры конкретного контрагента

Выборка = Справочники.ДоговорыКонтрагентов.Выбрать(, КонтрагентСсылка);

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

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

КонецЦикла;

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

Порядок действий для массовой обработки
  1. 1
    Определите цель: чтение данных или изменение элементов — от этого зависит выбор метода
  2. 2
    Напишите код с фильтрацией: исключите помеченные на удаление и группы, если они не нужны
  3. 3
    Протестируйте на копии базы с ограничением выборки (первые 50–100 элементов)
  4. 4
    Добавьте протоколирование: счётчик обработанных и журнал ошибок в цикле
  5. 5
    Запустите полную обработку в нерабочее время, при записи — порциями с фиксацией транзакций
  6. 6
    Проверьте результат выборочно и по контрольным итогам (количество изменённых элементов)

Типичные ошибки и ограничения

Первая группа ошибок — логические: в обработку попадают группы, помеченные на удаление элементы или предопределённые элементы, которые нельзя переименовывать без последствий для кода конфигурации. Всегда явно проверяйте ЭтоГруппа, ПометкаУдаления и Предопределенный, если они релевантны задаче.

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

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

Типичные ошибки
01
Обращение к реквизитам через Ссылка.Реквизит внутри цикла — запрос к базе на каждый элемент, обработка занимает часы
02
Запись всех элементов в одной транзакции — блокировка таблицы и откат всех изменений при любой ошибке
03
Отсутствие фильтра ЭтоГруппа — в обработку попадают папки и ломают логику
04
Изменение предопределённых элементов — перестают работать обращения к ним из кода конфигурации
05
Запуск обработки под администратором без учёта того, что регламентное задание работает под другим пользователем с RLS

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

После перебора с изменением данных сверьте количество обработанных элементов с ожидаемым: посчитайте записи запросом до и после. Если обработка заполняла реквизит, контрольный запрос вида ВЫБРАТЬ КОЛИЧЕСТВО(*) ... ГДЕ Реквизит = &Значение покажет, все ли элементы получили новое значение.

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

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

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

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

Совпадает с контрольным запросом по тем же условиям

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

Счётчик в протоколе меньше, чем записей в базе — часть элементов пропущена или вызвала ошибки

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

Выборочно открытые элементы содержат ожидаемые данные

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

Реквизит пуст или содержит старое значение — запись не выполнялась

Работа пользователей во время обработки
Норма

Нет жалоб на блокировки и зависания

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

Ошибки превышения времени ожидания блокировок — слишком крупные транзакции

FAQ

Как перебрать только элементы без групп?

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

Чем отличается Выбрать() от ВыбратьИерархически()?

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

Можно ли перебрать элементы справочника в фоновом задании?

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

Почему выборка возвращает не все элементы?

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

Как прервать перебор при нахождении нужного элемента?

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

Что быстрее для миллиона элементов — Выбрать() или запрос?

Запрос существенно быстрее, особенно если нужны конкретные реквизиты: он выполняется на стороне СУБД и передаёт только запрошенные поля. Метод Выбрать() оправдан для небольших справочников и случаев, когда важна простота кода или обход иерархии по уровням.

Итог

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

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