Работа с данными в конфигурациях на платформе 1С:Предприятие 8 невозможна без манипуляций со ссылками на объекты. Справочники являются основным хранилищем нормативно-справочной информации, и задача перебора их элементов возникает у разработчика постоянно. Будь то формирование сложного отчета, пакетное обновление реквизитов или интеграция с внешними системами, выбор правильного метода итерации напрямую влияет на скорость работы программы.
Существует несколько фундаментально разных подходов к решению этой задачи: от простого использования встроенного механизма выборки до написания сложных запросов на языке 1СКД. Неправильный выбор инструмента может привести к критическому замедлению системы при увеличении объема базы данных. В этой статье мы детально разберем каждый метод, его преимущества и подводные камни, чтобы вы могли писать эффективный код.
Использование объекта ВыборкаСправочника
Самый простой и интуитивно понятный способ получить доступ ко всем записям — это создание объекта ВыборкаСправочника. Этот метод идеально подходит для небольших справочников или ситуаций, когда требуется обработать каждый элемент без сложных условий фильтрации. Код получается лаконичным и легко читаемым даже для начинающих программистов.
Для инициализации выборки достаточно вызвать метод Выбрать() у объекта справочника. Важно понимать, что в момент вызова этого метода формируется курсор базы данных, который начинает последовательно проходить по всем узлам. Если в процессе обработки вы будете менять структуру справочника, это может привести к непредсказуемому поведению курсора.
Вот классический пример реализации такого подхода:
СправочникОбъект = Справочники.Номенклатура;
Выборка = СправочникОбъект.Выбрать();
Пока Выборка.Следующий() Цикл
// Обработка текущего элемента
Сообщение = "Обработан элемент: " + Выборка.Наименование;
КонецЦикла;
Однако у этого метода есть существенный недостаток — производительность. При работе с большими объемами данных (десятки или сотни тысяч записей) использование простой выборки может занять значительное время. Это связано с тем, что платформе приходится поддерживать состояние курсора и последовательно загружать каждый объект в память.
⚠️ Внимание: Никогда не используйте метод Выбрать() внутри циклов или в высоконагруженных регламентных заданиях без предварительной оценки объема данных. Это частая причина зависания сервера 1С в часы пик.
Если вам нужно выбрать только активные элементы или отфильтровать их по какому-то простому признаку, лучше сразу использовать параметры выборки. Это позволит отсеять лишние записи еще на уровне СУБД, не загружая их в память приложения.
Используйте метод Выборка.Следующий() вместо перебора по индексу — это гарантирует корректную работу с иерархическими справочниками и группами.
Оптимизация через язык запросов 1С
Для профессиональной разработки стандартом де-факто является использование языка запросов. Этот подход дает максимальную гибкость и позволяет переложить основную работу по фильтрации и сортировке на сторону сервера баз данных (MS SQL, PostgreSQL или Oracle). Запрос выбирает только нужные поля, что снижает нагрузку на сеть и оперативную память.
Основное преимущество запросов заключается в возможностиjoins (соединений) и агрегации данных еще до того, как они попадут в код 1С. Вы можете сразу получить выборку, содержащую ссылки на справочники и связанные с ними регистры сведений. Это избавляет от необходимости делать дополнительные обращения к базе внутри цикла обработки.
Типичная структура запроса для перебора элементов выглядит следующим образом:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ЛОЖЬ";
Результат = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = Результат.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Работа с полями выборки
КонецЦикла;
Важно отметить разницу между получением ссылки и получением объекта. В запросе мы чаще всего выбираем поле Ссылка, которая является легковесным объектом. Если внутри цикла вам потребуется изменить реквизиты элемента, ссылку нужно будет превратить в объект справочника методом ПолучитьОбъект(), что является дорогостоящей операцией.
Использование запросов также позволяет эффективно работать с виртуальными таблицами и срезами регистров. Вы можете одним запросом получить состояние расчетов на конкретную дату, что при использовании обычной выборки потребовало бы тысяч отдельных обращений к базе данных.
Работа с иерархией и обход дерева
Многие справочники в 1С имеют иерархическую структуру, где элементы вложены в группы. Стандартная выборка проходит по всем элементам "плоским" списком, игнорируя уровень вложенности. Однако иногда критически важно соблюдать порядок обхода: сначала родитель, потом дети, или наоборот.
Для решения таких задач используется свойство ИспользоватьИерархию объекта выборки. Установив его в значение Истина, вы заставляете курсор двигаться по дереву узлов. При этом доступен специальный метод ТекущийУровеньИерархии(), который показывает глубину вложенности текущего элемента.
Рассмотрим пример обхода дерева с учетом уровней:
- 🌳 Уровень 0: Корневые группы справочника.
- 📂 Уровень 1: Элементы или подгруппы, находящиеся непосредственно в корне.
- 📁 Уровень N: Вложенные элементы, глубина которых зависит от структуры.
При включенной иерархии метод Следующий() будет возвращать элементы в порядке их физического расположения в дереве. Это удобно для формирования печатных форм или отчетов, где важна группировка "от общего к частному". Но помните, что такой обход может быть медленнее плоского, так как СУБД должна выстраивать порядок выборки согласно структуре дерева.
⚠️ Внимание: При изменении структуры справочника (перемещение элементов между группами) во время обхода с включенной иерархией поведение выборки становится неопределенным. Рекомендуется сначала собрать список ссылок, а затем обрабатывать их.
Если вам нужно обработать только элементы определенной ветки, не проходя по всему справочнику, используйте метод ВыбратьИерархически() с указанием родительского элемента. Это существенно сократит область поиска и ускорит выполнение кода.
Пакетное чтение и производительность
Когда объем данных исчисляется миллионами записей, даже оптимизированный запрос может работать медленно из-за накладных расходов на передачу данных между сервером 1С и клиентом, либо между разными серверами в кластере. В таких случаях на помощь приходит механизм Пакетного чтения.
Суть метода заключается в том, что данные выбираются не по одной записи, а большими блоками (пакетами). Это позволяет минимизировать количество сетевых обращений и более эффективно использовать буферы памяти. Пакетное чтение особенно актуально для фоновых заданий, выгрузок данных и конвертации информации.
Для реализации этого подхода используется объект ЧтениеДанных. Он работает быстрее стандартной выборки, но имеет ограничение: вы получаете данные только для чтения. Изменять объекты напрямую через этот механизм нельзя, нужно сначала получить ссылку, а затем отдельным вызовом записать изменения.
| Метод | Скорость | Возможность записи | Сложность реализации |
|---|---|---|---|
| ВыборкаСправочника | Низкая | Да (через ПолучениеОбъекта) | Низкая |
| Запрос + Выборка | Средняя/Высокая | Да (через ПолучениеОбъекта) | Средняя |
| ЧтениеДанных | Очень высокая | Нет (только чтение) | Высокая |
Настройка размера пакета осуществляется через свойство РазмерПакета. Экспериментальным путем можно подобрать оптимальное значение для вашей инфраструктуры. Слишком маленький пакет не даст выигрыша в скорости, а слишком большой может привести к перерасходу оперативной памяти сервера.
Тонкости настройки ЧтенияДанных
Для максимального ускорения отключайте контроль целостности ссылок и не запрашивайте лишние реквизиты. Указывайте в полях чтения только те данные, которые реально используются в алгоритме.
Обработка помеченных на удаление объектов
Одной из частых задач администрирования и сопровождения 1С является очистка базы от "мусора". Элементы справочников, помеченные на удаление, по умолчанию скрываются из большинства выборок и интерфейсов. Однако для их физического удаления или восстановления необходимо явно перебрать именно эти записи.
Стандартная выборка игнорирует помеченные объекты. Чтобы получить к ним доступ, необходимо установить специальное свойство ПометкаУдаления в режим ТолькоПомеченныеНаУдаление или Все. Это позволяет отделить "живые" данные от тех, что готовы к утилизации.
Пример кода для поиска таких элементов:
Выборка = Справочники.Контрагенты.Выбрать();
Выборка.ПометкаУдаления = РежимПометкиУдаленияОбъектов.ТолькоПомеченныеНаУдаление;
Пока Выборка.Следующий() Цикл
// Логика восстановления или окончательного удаления
КонецЦикла;
Важно различать логическое удаление (пометка) и физическое удаление. Перебор помеченных объектов не удаляет их автоматически. Для физической очистки используется механизм удаления помеченных объектов, который также можно запустить программно, но это требует прав администратора и блокировки работы пользователей.
⚠️ Внимание: Интерфейсы и состав доступных полей могут меняться в разных версиях платформы 1С. Всегда сверяйте актуальные свойства объектов в синтаксис-помощнике вашей конкретной конфигурации перед написанием кода работы с удалением.
Частые ошибки и антипаттерны
Даже опытные разработчики иногда допускают ошибки при организации циклов перебора. Самая распространенная проблема — модификация коллекции во время итерации. Если вы удаляете элемент справочника или меняете его владельца (родителя) внутри цикла Пока Выборка.Следующий(), вы рискуете сбить курсор выборки.
Это может привести к тому, что некоторые элементы будут пропущены, а обработка зациклится на одной записи. Правильный подход в таких случаях — сначала собрать все ссылки, требующие изменения, в массив или временную таблицу, а уже потом, после завершения выборки, обработать этот список.
Еще один антипаттерн — выполнение запросов внутри цикла. Никогда не делайте так:
- ❌ Плохо: Внутри цикла выборки элементов номенклатуры делать новый запрос к регистру цен для каждого товара.
- ✅ Хорошо: Сделать один общий запрос с соединением (JOIN), который сразу вернет товары и их цены.
Такой подход, называемый "N+1 запрос", убивает производительность системы. Вместо одной операции база данных вынуждена выполнять тысячи, что создает огромную нагрузку на диск и процессор сервера.
Золотое правило оптимизации: минимизируйте количество обращений к базе данных. Один сложный запрос всегда лучше, чем тысяча простых внутри цикла.
FAQ: Вопросы и ответы
Как перебрать только элементы без групп?
Для этого в запросе добавьте условие ГДЕ Справочник.ЭтоГруппа = ЛОЖЬ. Если используете простую выборку, проверяйте свойство Выборка.ЭтоГруппа внутри цикла и пропускайте итерацию, если оно истинно.
Можно ли изменять реквизиты элемента прямо в выборке?
Нет, объект выборки доступен только для чтения. Чтобы изменить данные, нужно получить объект справочника по ссылке: Объект = Выборка.Ссылка.ПолучитьОбъект(), внести изменения и вызвать Объект.Записать().
Что делать, если выборка работает слишком медленно?
Попробуйте перейти на язык запросов с отбором нужных полей. Если это не помогает, используйте механизм ЧтениеДанных с настройкой размера пакета. Также проверьте наличие индексов в базе данных для полей, по которым идет отбор.
Как получить количество элементов без полного перебора?
Не используйте цикл для подсчета! В запросе используйте агрегатную функцию КОЛИЧЕСТВО(*). Для простой выборки есть свойство Количество(), но оно также может требовать прохода по данным в некоторых реализациях, поэтому запрос предпочтительнее.