Работа с массивами объектов в 1С:Предприятие 8.3 — одна из самых востребованных задач при разработке конфигураций, отчетов и обработок.hether вы только начинаете осваивать платформу или уже опытный программист, умение эффективно получать collections объектов сэкономит часы работы и убережет от типичных ошибок. В этой статье мы разберем все актуальные способы: от элементарного перебора справочников до сложных запросов с joins и группировками.
Особое внимание уделим производительности каждого метода — этот аспект часто упускают, хотя от него зависит скорость работы ваших решений на больших базах. Например, выборка 10 000 документов через ПолучитьСписок() и через запрос может отличаться по времени в десятки раз. Мы также рассмотрим нюансы работы с управляемыми формами, обычными формами и серверными процедурами, где подходы к получению данных принципиально разные.
Статья будет полезна как начинающим разработчикам, так и тем, кто хочет оптимизировать существующий код. Все примеры приведены для актуальной версии платформы 1С:Предприятие 8.3.23 (и выше), но большинство методов работают и в более ранних релизах. Если вы используете 1С:EDT или другие инструменты разработки, принципы остаются теми же — меняется только синтаксис вызова.
1. Базовые методы: Получение массива через коллекции объектов
Начнем с самого простого — работы со встроенными коллекциями платформы. Этот способ подходит для небольших объемов данных (до 1 000–2 000 объектов), когда не требуется сложная фильтрация или сортировка.
Основные методы, которые вам пригодятся:
- 📋
Справочник.Номенклатура.ПолучитьСписок()— возвращает массив всех элементов справочника без фильтров. Внимание: на больших справочниках вызовет зависание! - 🔍
Документ.РеализацияТоваровУслуг.Выбрать()— создает выборку документов с возможностью фильтрации по дате, контрагенту и другим реквизитам. - 📊
Массив = Новый Массив;+Массив.Добавить(Объект)— ручное формирование массива в цикле. Полезно, когда нужно отобрать объекты по сложным условиям. - 🔄
Объект.ПолучитьОбъекты()— альтернативаПолучитьСписок()для некоторых типов объектов (например, планы видов характеристик).
Пример кода для получения всех активных номенклатурных позиций:
МассивНоменклатуры = Справочник.Номенклатура.ПолучитьСписок();
Для Каждого Элемент Из МассивНоменклатуры Цикл
Если НЕ Элемент.ПометкаУдаления Тогда
АктивныеПозиции.Добавить(Элемент);
КонецЕсли;
КонецЦикла;
⚠️ Внимание: МетодПолучитьСписок()загружает все реквизиты объектов в память, что может привести к переполнению на больших базах. Для выборки только нужных полей используйтеВыбрать()с указанием отбираемых реквизитов.
Если вам нужны только ссылки на объекты (без данных), используйте конструкцию Справочник.Номенклатура.ПолучитьСсылки() — это ускорит выполнение в 2–3 раза.
2. Запросы как универсальный инструмент
Язык запросов 1С — самый мощный и гибкий способ получения массивов объектов. Он позволяет:
- 🔗 Соединять данные из нескольких таблиц (
ЛЕВОЕ СОЕДИНЕНИЕ,ВНУТРЕННЕЕ СОЕДИНЕНИЕ) - 📈 Агрегировать данные (
СУММА,КОЛИЧЕСТВО,МАКСИМУМ) - 🔎 Фильтровать по сложным условиям (
ГДЕс вложенными запросами) - 📊 Сортировать и группировать результаты
Базовый шаблон запроса для получения массива документов:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТоваровУслуг.Ссылка КАК Ссылка,
| РеализацияТоваровУслуг.Дата КАК Дата,
| РеализацияТоваровУслуг.Контрагент КАК Контрагент
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
| РеализацияТоваровУслуг.Дата МЕЖДУ &НачалоПериода И &КонецПериода
|УПОРЯДОЧИТЬ ПО
| Дата УБЫВ";
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДата()));
РезультатЗапроса = Запрос.Выполнить();
МассивДокументов = РезультатЗапроса.Выгрузить();
Ключевые преимущества запросов:
| Характеристика | Коллекции объектов | Запросы |
|---|---|---|
| Производительность на больших данных | Низкая | Высокая |
| Сложная фильтрация | Требует ручного кода | Встроенная поддержка |
| Объединение данных из нескольких источников | Невозможно | Легко реализуется |
| Агрегация (суммы, средние) | Требует дополнительных циклов | Встроенные функции |
| Память | Загружает все реквизиты | Можно выбрать только нужные поля |
3. Оптимизация производительности: что ускоряет, а что тормозит
Даже правильно написанный код может работать медленно, если не учитывать особенности платформы. Вот ключевые факторы, влияющие на скорость:
Что замедляет выполнение:
- 🐢 Получение всех реквизитов вместо нужных:
ПолучитьСписок()vsВыбрать(Реквизит1, Реквизит2) - 🔄 Вложенные циклы по большим коллекциям (например, перебор 10 000 документов с проверкой каждого товара)
- 🗄️ Частые обращения к базе в цикле: лучше загрузить данные один раз в массив, а потом работать с ним.
- 📉 Отсутствие индексов на полях, по которым идет фильтрация в запросах.
Как ускорить:
- ⚡ Используйте
ПОМЕСТИТЬв запросах для временных таблиц с промежуточными результатами. - 📌 Применяйте
ИНДЕКСИРОВАТЬ ПОдля полей, по которым часто фильтруете данные. - 🔧 Разбивайте сложные запросы на несколько простых с использованием
ОБЪЕДИНИТЬ. - 🖥️ Для серверных процедур используйте
НаСервере— это уменьшает нагрузку на клиент.
Пример оптимизированного запроса с временной таблицей:
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Товары.Ссылка КАК Ссылка
|ПОМЕСТИТЬ ВТТовары
|ИЗ
| Справочник.Номенклатура КАК Товары
|ГДЕ
| Товары.ЭтоГруппа = ЛОЖЬ
| И Товары.ПометкаУдаления = ЛОЖЬ;
|
|////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТТовары.Ссылка КАК Ссылка,
| ВТТовары.Артикул КАК Артикул
|ИЗ
| ВТТовары КАК ВТТовары";
⚠️ Внимание: Начиная с версии 8.3.20, платформа автоматически кэширует результаты некоторых запросов. Однако это не отменяет необходимости оптимизации — кэш сбрасывается при изменении данных или перезапуске сеанса.
☑️ Проверка оптимизации запроса
4. Работа с динамическими списками и управляемыми формами
В управляемых формах (а также в обычных формах с динамическими списками) получение массива объектов имеет свои особенности. Здесь важно понимать разницу между:
- 📜 Статическим массивом — данные загружаются один раз (например, при открытии формы).
- 🔄 Динамическим списком — данные подгружаются порциями при прокрутке (ленивая загрузка).
Пример получения данных для динамического списка в управляемой форме:
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 100
| Контрагенты.Ссылка КАК Ссылка,
| Контрагенты.Наименование КАК Наименование
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|УПОРЯДОЧИТЬ ПО
| Наименование";
Результат = Запрос.Выполнить();
ДинамическийСписок.ЗагрузитьДанные(Результат.Выгрузить());
КонецПроцедуры
Для работы с большими списками (более 1 000 строк) рекомендуется:
- Использовать постраничную загрузку (
ВЫБРАТЬ ПЕРВЫЕ N+ кнопка "Загрузить еще"). - Отключать автоматическое обновление списка при изменении данных (
ДинамическийСписок.АвтоОбновление = Ложь). - Применять серверный поиск вместо клиентского фильтра.
Как отладить медленный динамический список?
1. Включите профилировщик запросов в конфигураторе (Сервис → Профилировщик запросов). 2. Проверьте, не загружаются ли лишние реквизиты в выборке. 3. Убедитесь, что фильтрация происходит на сервере, а не на клиенте. 4. Для списков более 5 000 строк рассмотрите возможность использования отчетов с СКД вместо динамических списков.
5. Типичные ошибки и как их избежать
Даже опытные разработчики иногда сталкиваются с проблемами при работе с массивами объектов. Вот самые распространенные ошибки и способы их решения:
Ошибка 1: "Недостаточно памяти"
Причина: Попытка загрузить в массив все документы за 5 лет (например, 50 000 записей).
Решение:
- 📅 Разбивайте выборку по периодам (месяц/квартал).
- 🗃️ Используйте
ВыгрузитьДанные()с ограничением по количеству строк. - 🔧 Перепишите логику на использование
ПостроительОтчетавместо массива.
Ошибка 2: "Объект не найден" при работе с ссылками
Причина: В массиве хранятся ссылки на объекты, которые были удалены или перенесены в архив.
Решение:
Для Каждого Ссылка Из МассивСсылок Цикл
Попытка
Объект = Ссылка.ПолучитьОбъект();
// Обработка объекта
Исключение
Сообщить("Объект не найден: " + Ссылка.Наименование);
КонецПопытки;
КонецЦикла;
Ошибка 3: Медленная сортировка массива
Причина: Сортировка массива из 10 000 элементов с использованием Массив.Сортировать() без указания метода сравнения.
Решение:
Процедура СравнитьЭлементы(Элемент1, Элемент2)
Возврат ?(Элемент1.Дата > Элемент2.Дата, 1, ?(Элемент1.Дата = Элемент2.Дата, 0, -1));
КонецПроцедуры
МассивДокументов.Сортировать(АдресПроцедуры("СравнитьЭлементы"));
⚠️ Внимание: При работе с распределенными информационными базами (РИБ) или обменами данными всегда проверяйте, что объекты в массиве принадлежат текущей базе. Использование ссылок из другой базы приведет к ошибке "Неверный формат данных".
6. Продвинутые техники: группировка, кэширование и асинхронность
Для сложных задач стандартных методов может быть недостаточно. Рассмотрим продвинутые подходы:
1. Группировка данных в запросе
Если вам нужно получить массив объектов с агрегированными данными (например, суммы продаж по контрагентам), используйте ГРУППИРОВКА ПО:
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТоваровУслуг.Контрагент КАК Контрагент,
| СУММА(РеализацияТоваровУслуг.СуммаДокумента) КАК ИтогоПродаж
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
| РеализацияТоваровУслуг.Дата МЕЖДУ &Начало И &Конец
|СГРУППИРОВАТЬ ПО
| РеализацияТоваровУслуг.Контрагент";
2. Кэширование результатов
Если один и тот же массив объектов запрашивается многократно (например, в отчете), сохраняйте его в МенеджерВременныхДанных:
Процедура ПолучитьКэшированныеДанные()
Если НЕ МенеджерВременныхДанных.Существует("МассивКонтрагентов") Тогда
Массив = ПолучитьМассивКонтрагентовИзБазы();
МенеджерВременныхДанных.Вставить("МассивКонтрагентов", Массив);
КонецЕсли;
Возврат МенеджерВременныхДанных.Получить("МассивКонтрагентов");
КонецПроцедуры
3. Асинхронная загрузка данных
Для управляемых форм можно использовать ПоказатьОповещениеПользователя() с фоновой загрузкой:
Процедура ЗагрузитьДанныеАсинхронно()
Оповещение = ПоказатьОповещениеПользователя("Идет загрузка данных...", 0);
АсинхроннаяПроцедура Загрузка()
МассивДанных = ДолгийЗапросКБазе();
Оповещение.Закрыть();
ЗаполнитьТаблицуДанными(МассивДанных);
КонецПроцедуры
Загрузка();
КонецПроцедуры
Эти техники особенно полезны для:
- 📊 Отчетов с большим объемом данных.
- 🖥️ Веб-сервисов и HTTP-сервисов, где важна скорость ответа.
- 📱 Мобильных приложений на платформе 1С:Мобильная платформа.
Кэширование и асинхронная загрузка могут сократить время ожидания пользователя в 5–10 раз, но требуют аккуратной реализации, чтобы избежать утечек памяти.
7. Альтернативные подходы: внешние источники и интеграции
Иногда данные нужно получить не из текущей базы, а из внешних источников. Рассмотрим основные сценарии:
1. Получение данных из другой базы 1С
Используйте COMСоединение или веб-сервисы:
Соединение = Новый COMОбъект("V83.COMConnector");
Подключение = Соединение.Connect("File=""C:\Bases\ExternalBase"";Usr=""Администратор"";");
Запрос = Подключение.NewObject("Запрос");
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 100 Справочник.Контрагенты.Ссылка КАК Ссылка";
Результат = Запрос.Выполнить();
Массив = Результат.Выгрузить();
2. Импорт из Excel или JSON
Для загрузки данных из файлов:
// Чтение JSON
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ПолучитьТекстИзФайла("data.json"));
МассивДанных = ПрочитатьJSON(ЧтениеJSON);
// Чтение Excel через OLE
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.Workbooks.Open("C:\data.xlsx");
Лист = Книга.Worksheets(1);
Данные = Лист.UsedRange.Value;
3. Работа с HTTP-сервисами
Пример получения данных из REST API:
HTTPСоединение = Новый HTTPСоединение("api.example.com", 80);
Запрос = Новый HTTPЗапрос("/v1/products");
Ответ = HTTPСоединение.Получить(Запрос);
МассивТоваров = JSON.Прочитать(Ответ.ПолучитьТекст());
⚠️ Внимание: При работе с внешними источниками всегда обрабатывайте исключения (ошибки сети, неверный формат данных, таймауты). Используйте Попытка...Исключение для предотвращения падения программы.
FAQ: Ответы на частые вопросы
Как получить массив объектов с отбором по нескольким условиям?
Используйте комбинацию методов Выбрать() с фильтрами или запрос с несколькими условиями в секции ГДЕ:
Выборка = Справочник.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.Цена > 1000 И Выборка.Остаток > 0 Тогда
Массив.Добавить(Выборка.Ссылка);
КонецЕсли;
КонецЦикла;
Для сложных условий лучше использовать запрос с операторами И, ИЛИ, В().
Можно ли получить массив объектов из регистра накопления?
Да, но только через запрос. Пример для регистра ТоварыНаСкладах:
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
| ТоварыНаСкладахОстатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки";
Для виртуальных таблиц (Остатки, Обороты) обязательно указывайте период отбора.
Как экспортировать массив объектов в Excel?
Используйте Библиотеку стандартных подсистем (БСП) или COMОбъект("Excel.Application"):
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.Workbooks.Add();
Лист = Книга.Worksheets(1);
// Заполнение заголовков
Лист.Cells(1, 1).Value = "Наименование";
Лист.Cells(1, 2).Value = "Артикул";
// Заполнение данных
Для i = 0 По Массив.ВГраница() Цикл
Лист.Cells(i + 2, 1).Value = Массив[i].Наименование;
Лист.Cells(i + 2, 2).Value = Массив[i].Артикул;
КонецЦикла;
Книга.SaveAs("C:\Export.xlsx");
Excel.Quit();
Для больших массивов (>10 000 строк) лучше использовать ADODB.Stream для записи в CSV.
Почему запрос возвращает пустой массив, хотя данные есть?
Частые причины:
- Ошибка в условии отбора (например, неверный формат даты:
Дата = '2023-12-31'вместоДата = ДАТАВРЕМЯ(2023, 12, 31)). - Отсутствуют права у пользователя на чтение данных.
- В запросе используются несуществующие поля или псевдонимы.
- Для регистров не указан период или неверно выбрана виртуальная таблица.
Включите отладку запроса (Запрос.Отладка = Истина;) для вывода исполняемого SQL-кода.
Как получить массив объектов с иерархией (дерево справочника)?
Используйте рекурсивный обход или запрос с ПУТЬ:
Процедура ПолучитьДерево(Родитель, Массив)
Выборка = Справочник.Номенклатура.Выбрать(Родитель);
Пока Выборка.Следующий() Цикл
Массив.Добавить(Выборка.Ссылка);
ПолучитьДерево(Выборка.Ссылка, Массив); // Рекурсия
КонецЦикла;
КонецПроцедуры
// Или через запрос:
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| ПУТЬ(Номенклатура.Ссылка) КАК Путь
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";