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

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

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

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

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

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

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

Базовый перебор через метод Выбрать()

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

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

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

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

КонецЦикла;

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

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

bsl
Выборка = Справочники.Контрагенты.Выбрать();
Пока Выборка.Следующий() Цикл
    Если Выборка.ПометкаУдаления Тогда
        Продолжить;
    КонецЕсли;
    Объект = Выборка.ПолучитьОбъект();
    Объект.Комментарий = "Обработан";
    Объект.Записать();
КонецЦикла;

Перебор с отбором и порядком

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

Отбор = Новый Структура;

Отбор.Вставить("СтавкаНДС", Перечисления.СтавкиНДС.НДС20);

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

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

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

КонецЦикла;

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

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

Перебор элементов запросом

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

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

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

"ВЫБРАТЬ

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

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

|ИЗ

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

|ГДЕ

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

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

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

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

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

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

КонецЦикла;

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

Результат запроса можно обходить не только методом Выбрать(), но и через Выгрузить() в таблицу значений — это удобно, когда данные нужно несколько раз перебирать, сортировать по-разному или искать строки методом Найти() и НайтиСтроки().

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

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

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

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

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

Запрос

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

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

Когда результат нужен многократно: поиск, сортировка, повторные обходы без повторного обращения к базе.

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

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

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

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

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

Отступ = "";

Для Н = 1 По Уровень Цикл

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

КонецЦикла;

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

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

КонецЦикла;

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

// Вызов: ОбойтиДерево(Справочники.Подразделения.ПустаяСсылка(), 0);

Альтернатива рекурсии — запрос с итогами по иерархии или обход результата запроса методом Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией). Такой подход быстрее на больших деревьях, так как вся структура выгружается одним обращением к базе.

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

Порядок действий при обходе дерева
  1. 1
    Определите тип иерархии справочника в конфигураторе: иерархия групп и элементов или иерархия элементов
  2. 2
    Напишите рекурсивную процедуру с параметром Родитель или запрос с иерархическим обходом
  3. 3
    Внутри цикла отсекайте группы проверкой ЭтоГруппа, если они не нужны
  4. 4
    Проверьте результат на тестовой копии базы: порядок вывода и полнота охвата всех веток

Безопасное изменение элементов при переборе

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

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

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

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

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

К типичным ошибкам относятся: обращение к реквизитам выборки, которых нет в списке полей запроса (ошибка «Поле не обнаружено»); забытая проверка ЭтоГруппа в иерархических справочниках; попытка использовать отбор по неиндексированному реквизиту; и перебор всей номенклатуры методом Выбрать() с получением объекта каждого элемента там, где достаточно одного запроса.

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

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

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

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

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

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

Счётчик меньше — часть элементов потеряна из-за фильтров или пометок удаления

Состав выборки
Норма

Нет помеченных на удаление и групп, если они не нужны

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

В обработку попадают папки или удалённые элементы

Скорость выполнения
Норма

Обработка завершается за приемлемое время без блокировок

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

Запись каждого объекта в цикле надолго блокирует таблицу

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

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

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

Как перебрать только элементы, не помеченные на удаление?

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

Как перебрать элементы подчинённого справочника по владельцу?

Передайте владельца вторым параметром: Справочники.ДоговорыКонтрагентов.Выбрать(, КонтрагентСсылка). Первый параметр (Родитель) оставьте незаполненным, поставив запятую.

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

Можно через ПолучитьОбъект() и Записать(), но небезопасно менять поля, входящие в отбор или сортировку. Надёжнее сначала собрать ссылки в массив, а затем изменять элементы по ним отдельным проходом.

Как обойти дерево иерархического справочника с отступами по уровням?

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

Почему отбор в Выбрать() выдаёт ошибку по моему реквизиту?

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

Итог

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

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