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

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

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

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

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

Выбор способа влияет не только на скорость выполнения, но и на нагрузку на сервер СУБД и на объём передаваемых данных между клиентом и сервером. Неправильно написанный цикл по большому справочнику способен «повесить» сеанс пользователя на десятки минут, а при обмене данными — привести к блокировкам. Ниже разобраны рабочие варианты с примерами кода, условиями выбора и типичными ошибками.

Все примеры приведены на встроенном языке 1С и подходят для управляемых и обычных форм. Там, где поведение отличается между толстым и тонким клиентом, это оговорено отдельно.

Способ 1: перебор через метод Выбрать()

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

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

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

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

Продолжить;

КонецЕсли;

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

КонецЦикла;

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

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

Способ 2: перебор через запрос

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

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

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

"ВЫБРАТЬ

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

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

|ИЗ

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

|ГДЕ

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

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

|УПОРЯДОЧИТЬ ПО

| Наименование";

Выборка = Запрос.Выполнить().Выбрать();

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

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

КонецЦикла;

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

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

Сравнение вариантов

Выбрать()

Подходит для изменения элементов в цикле и простых отборов. Медленнее запроса при чтении больших объёмов.

Запрос

Быстрое чтение, гибкие отборы и соединения. Не подходит для прямой записи — нужен ПолучитьОбъект().

Выгрузить() в таблицу значений

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

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

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

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

Процедура ОбойтиУровень(Родитель, Уровень)

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

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

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

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

ОбойтиУровень(Выборка.Ссылка, Уровень + 1);

КонецЕсли;

КонецЦикла;

КонецПроцедуры

Более производительная альтернатива — запрос с конструкцией ИТОГИ ПО Родитель ТОЛЬКО ИЕРАРХИЯ или обход результата запроса методом Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией). Один запрос читает всю таблицу, а обход группировок восстанавливает дерево уже в памяти.

Порядок действий для обхода дерева запросом
  1. 1
    Сформируйте запрос по таблице справочника с полями Ссылка, Наименование и ИТОГИ ПО Ссылка ТОЛЬКО ИЕРАРХИЯ
  2. 2
    Выполните Выборка = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией)
  3. 3
    В цикле Пока Выборка.Следующий() обрабатывайте элемент и при необходимости получайте вложенную выборку через Выборка.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией)
  4. 4
    Проверьте результат на тестовой группе с известным числом элементов

Отборы, отсев групп и помеченных на удаление

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

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

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

Производительность и работа на клиенте и сервере

Перебор справочника — серверная операция. В управляемом приложении код обхода должен выполняться на сервере: директивы &НаСервере, &НаСервереБезКонтекста или серверные общие модули. Вызов сервера из клиентского цикла на каждой итерации — классическая ошибка, из-за которой обработка тысячи элементов растягивается на минуты: каждый вызов — это сетевой round-trip.

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

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

Типичные ошибки
01
Вызов серверных функций внутри клиентского цикла — обработка замедляется в десятки раз из-за сетевых вызовов
02
Получение объекта через ПолучитьОбъект() только ради чтения реквизитов — лишние обращения к базе и риск блокировок
03
Фильтрация элементов оператором Если внутри цикла вместо отбора в запросе — перебираются лишние записи
04
Забытая проверка ЭтоГруппа — в обработку попадают группы, что ломает логику или вызывает ошибки записи
05
Модификация справочника в цикле без транзакции — при сбое данные остаются изменёнными частично

Проверка результата и безопасная отладка

Перед запуском массового перебора на рабочей базе убедитесь, что логика отработана на копии. Минимальный контроль: посчитайте количество элементов выборки методом Количество() у результата запроса или через отдельный запрос с СУММА/КОЛИЧЕСТВО и сравните с ожидаемым числом. Расхождение — признак ошибки в отборе.

При отладке удобно ограничить выборку первыми десятью элементами (ПЕРВЫЕ 10 в запросе или счётчик в цикле с Прервать). Это позволяет проверить корректность обращения к реквизитам и формат вывода без ожидания полного обхода. После успешной проверки ограничение снимается.

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

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

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

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

Совпадает с числом из контрольного запроса с теми же отборами

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

Меньше ожидаемого — часть элементов отсечена ошибочным условием

Группы в результате
Норма

Группы присутствуют только если это предусмотрено логикой

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

Группы попадают в обработку элементов и вызывают ошибки

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

Обход выполняется за секунды или минуты без роста нагрузки на СУБД

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

Резкое замедление, блокировки, таймауты сеансов

Частые вопросы о переборе справочников в 1С

Чем отличается выборка от объекта справочника?

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

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

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

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

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

Что быстрее: Выбрать() или запрос?

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

Как прервать перебор по условию?

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

Опасно ли изменять элементы справочника внутри цикла перебора?

Да, если делать это без транзакции и без теста на копии. Ошибка в середине цикла оставит часть элементов изменёнными. Оборачивайте массовые изменения в транзакции, фиксируйте порциями и всегда проверяйте обработку на копии базы.

Итог

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

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