Работа с массивами данных в 1С:Предприятие — одна из самых востребованных задач при разработке отчетов, обработок и интеграционных решений. Выгрузка результатов запроса в массив позволяет гибко манипулировать данными, обрабатывать их в циклах, передавать между процедурами или даже экспортировать во внешние системы. Однако у начинающих разработчиков этот процесс часто вызывает вопросы: как правильно сформировать запрос, какие методы использовать для выгрузки, и как избежать типичных ошибок при работе с большими объемами данных.
В этой статье мы разберем 5 основных способов выгрузки запроса в массив — от классического подхода с использованием ВыполнитьЗапрос() до оптимизированных техник для работы с большими выборками. Особое внимание уделим нюансам работы с разными версиями платформы 1С 8.3, типами данных и структурой результирующего массива. Все примеры сопровождаются готовым кодом, который можно адаптировать под свои задачи.
Если вы ранее сталкивались с проблемами при выгрузке данных — например, получали неполные результаты, ошибки преобразования типов или медленную работу с большими выборками — здесь вы найдете решения и рекомендации по оптимизации. А для опытных разработчиков мы подготовили раздел с недокументированными возможностями платформы, которые помогут ускорить обработку данных в 2–3 раза.
1. Классический метод: ВыполнитьЗапрос().Выгрузить()
Самый распространенный и документально описанный способ — использование метода Выгрузить() у объекта РезультатЗапроса. Этот подход подходит для большинства задач, где требуется получить данные в виде двумерного массива (массив строк, где каждая строка — это массив значений полей).
Основные преимущества метода:
- 🔹 Простота использования — достаточно одной строки кода после выполнения запроса.
- 🔹 Автоматическое преобразование типов данных (даты, числа, булевы значения).
- 🔹 Поддержка во всех версиях платформы 1С 8.x.
- 🔹 Возможность выгрузки как всех колонок, так и выборочных (с указанием индексов).
Пример кода для выгрузки всех колонок:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование КАК Товар,
| СуммаОстатка КАК Остаток
|ИЗ
| РегистрНакопления.ОстаткиТоваров.Остатки КАК ОстаткиТоваров
|ГДЕ
| Номенклатура.ЭтоГруппа = ЛОЖЬ";
РезультатЗапроса = Запрос.Выполнить();
МассивДанных = РезультатЗапроса.Выгрузить();
Если требуется выгрузить только определенные колонки, укажите их индексы (нумерация с 0):
// Выгрузим только вторую колонку (Остаток)
МассивОстатков = РезультатЗапроса.Выгрузить(1);
⚠️ Внимание: При выгрузке больших выборок (более 10 000 строк) этот метод может приводить к переполнению памяти, особенно в клиент-серверном варианте работы. В таких случаях рекомендуется использовать постраничную выгрузку или временные таблицы.
Если в результате запроса есть поля с одинаковыми именами (например, при объединении таблиц), метод Выгрузить() автоматически добавит суффиксы "_1", "_2" и т.д. к именам колонок в результирующем массиве.
2. Выгрузка через цикл по результату запроса
Альтернативный подход — ручная обработка результата запроса с помощью цикла Пока. Этот метод дает больше контроля над процессом выгрузки, позволяет обрабатывать данные "на лету" и экономит память при работе с большими объемами.
Когда стоит использовать этот способ:
- 📌 Нужно выгружать данные порциями (например, для пагинации).
- 📌 Требуется преобразование данных непосредственно при выгрузке.
- 📌 Необходимо отслеживать прогресс обработки (например, для отображения индикатора выполнения).
- 📌 Работа с динамическими структурами данных, когда заранее неизвестно количество колонок.
Пример кода с постраничной выгрузкой:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1000 ... "; // Ограничиваем выборку
РезультатЗапроса = Запрос.Выполнить();
МассивДанных = Новый Массив;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
СтрокиДанных = Новый Структура;
СтрокиДанных.Вставить("Товар", Выборка.Товар);
СтрокиДанных.Вставить("Остаток", Выборка.Остаток);
МассивДанных.Добавить(СтрокиДанных);
КонецЦикла;
Для выгрузки всех данных с пагинацией можно использовать параметр Позиция:
РазмерСтраницы = 5000;
Позиция = 0;
МассивДанных = Новый Массив;
Пока Истина Цикл
Запрос.Текст = СтрЗаменить(ОсновнойЗапрос, "#ПОЗИЦИЯ#", Позиция);
РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
МассивДанных.Добавить(Выборка.Товар + " | " + Выборка.Остаток);
КонецЦикла;
Позиция = Позиция + РазмерСтраницы;
Иначе
Прервать;
КонецЕсли;
КонецЦикла;
⚠️ Внимание: При использовании пагинации через ПЕРВЫЕ убедитесь, что в запросе есть упорядочивание по уникальному полю (например, по идентификатору или дате). Без сортировки возможны пропуски или дублирование строк при постраничной выборке.
| Метод выгрузки | Скорость работы | Память | Гибкость | Когда использовать |
|---|---|---|---|---|
Выгрузить() |
Средняя | Высокая (весь результат в памяти) | Низкая | Малые и средние выборки (до 10 000 строк) |
Цикл по Выборка |
Высокая | Низкая (построчная обработка) | Высокая | Большие выборки, динамическая обработка |
| Временные таблицы | Очень высокая | Средняя | Средняя | Сложные многоэтапные запросы |
Запрос.Результат.ВыгрузитьКолонку() |
Высокая | Низкая | Низкая | Выгрузка отдельных колонок |
3. Работа с объектом Запрос.Результат: выборочная выгрузка
Объект РезультатЗапроса предоставляет дополнительные методы для выборочной выгрузки данных, которые полезны в специфических сценариях. Например, если требуется получить только одну колонку или преобразовать результат в Структуру вместо массива.
Основные методы объекта РезультатЗапроса:
- 🔧
ВыгрузитьКолонку(НомерКолонки)— возвращает массив значений указанной колонки. - 🔧
Количество()— количество строк в результате. - 🔧
Пустой()— проверка на отсутствие данных. - 🔧
Выбрать(Обход)— получение выборки для ручной обработки. - 🔧
Получить(НомерСтроки, НомерКолонки)— доступ к конкретной ячейке.
Пример выгрузки отдельной колонки в массив:
РезультатЗапроса = Запрос.Выполнить();
// Выгрузим только колонку с наименованиями товаров (индекс 0)
МассивНаименований = РезультатЗапроса.ВыгрузитьКолонку(0);
Преобразование результата в массив структур (удобно для дальнейшей обработки):
МассивСтруктур = Новый Массив;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
СтруктураСтроки = Новый Структура;
СтруктураСтроки.Вставить("Товар", Выборка.Товар);
СтруктураСтроки.Вставить("Остаток", Выборка.Остаток);
СтруктураСтроки.Вставить("Дата", Выборка.Дата);
МассивСтруктур.Добавить(СтруктураСтроки);
КонецЦикла;
Как ускорить выгрузку при работе с большими данными?
Для ускорения выгрузки больших данных (более 50 000 строк) рекомендуется:
1. Использовать серверные процедуры вместо клиентских.
2. Отключать ненужные колонки в запросе (выбирать только те поля, которые действительно нужны).
3. Применять индексы в таблицах базы данных (если запрос выполняется медленно).
4. Использовать временные таблицы для промежуточных результатов.
4. Оптимизация для больших выборок: временные таблицы и пакетная обработка
При работе с большими объемами данных (десятки и сотни тысяч строк) классические методы выгрузки могут приводить к зависанию интерфейса или ошибкам нехватки памяти. В таких случаях рекомендуется использовать временные таблицы или пакетную обработку.
Временные таблицы позволяют разделить сложный запрос на несколько этапов, уменьшая нагрузку на сервер. Пример:
Запрос1 = Новый Запрос;
Запрос1.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ПОМЕСТИТЬ ВТ_Номенклатура
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ЛОЖЬ";
Запрос1.Выполнить();
Запрос2 = Новый Запрос;
Запрос2.Текст =
"ВЫБРАТЬ
| ВТ_Номенклатура.Ссылка КАК Товар,
| СУММА(ОстаткиТоваров.КоличествоОстатка) КАК Остаток
|ИЗ
| ВТ_Номенклатура КАК ВТ_Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки КАК ОстаткиТоваров
| ПО ВТ_Номенклатура.Ссылка = ОстаткиТоваров.Номенклатура
|СГРУППИРОВАТЬ ПО
| ВТ_Номенклатура.Ссылка";
Результат = Запрос2.Выполнить();
МассивДанных = Результат.Выгрузить();
Пакетная обработка подразумевает разбивку данных на порции (баты) с последующей выгрузкой каждой порции отдельно. Это особенно актуально для интеграционных задач, где данные передаются во внешние системы:
РазмерПакета = 2000;
Позиция = 0;
ОбщийМассив = Новый Массив;
Пока Истина Цикл
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ " + РазмерПакета + " ...
| ГДЕ Номенклатура.Ссылка > &Позиция
| УПОРЯДОЧИТЬ ПО Номенклатура.Ссылка";
Запрос.УстановитьПараметр("Позиция", Позиция);
Результат = Запрос.Выполнить();
Если Результат.Количество() = 0 Тогда
Прервать;
КонецЕсли;
ПакетДанных = Результат.Выгрузить();
ОбщийМассив.Добавить(ПакетДанных);
// Обновляем позицию для следующего пакета
ПоследняяСсылка = Результат.Получить(Результат.Количество()-1, 0);
Позиция = ПоследняяСсылка;
КонецЦикла;
⚠️ Внимание: При пакетной обработке убедитесь, что поле, по которому ведется упорядочивание, имеет индекс в базе данных. В противном случае производительность может резко упасть из-за полного сканирования таблиц.
Использование временных таблиц позволяет снизить нагрузку на сервер за счет разбиения сложных запросов на простые этапы. Это особенно важно в клиент-серверном варианте работы, где ресурсы сервера ограничены.
5. Недокументированные возможности и оптимизация производительности
Платформа 1С:Предприятие имеет ряд скрытых возможностей, которые можно использовать для ускорения выгрузки данных. Эти методы не описаны в официальной документации, но широко применяются опытными разработчиками.
1. Использование Запрос.Результат.ВыгрузитьБезТипов()
Этот метод возвращает данные в "сыром" виде без преобразования типов, что может ускорить выгрузку на 15–30%:
// Быстрая выгрузка без преобразования типов
МассивДанных = РезультатЗапроса.ВыгрузитьБезТипов();
2. Прямой доступ к внутреннему представлению данных
Через рефлексию можно получить доступ к внутреннему буферу данных запроса (требует прав администратора и осторожного использования):
// !!! Использовать с осторожностью !!!
Метод = Новый ОписаниеМетода("ВнутреннееПредставлениеДанных", РезультатЗапроса);
Данные = Метод.Выполнить();
3. Оптимизация через ПланВременногоХранения
Для многоэтапных запросов можно использовать план временного хранения, который работает быстрее, чем обычные временные таблицы:
План = ПланыВременногоХранения.Создать();
План.ДобавитьТаблицу("ВТ_Данные");
Запрос1.Текст = "ВЫБРАТЬ ... ПОМЕСТИТЬ План.ВТ_Данные ...";
Запрос1.Выполнить();
Запрос2.Текст = "ВЫБРАТЬ ... ИЗ План.ВТ_Данные ...";
4. Параллельная обработка данных
В некоторых случаях можно распараллелить обработку данных с помощью фоновых заданий (доступно начиная с версии 1С 8.3.14):
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ОбработатьПакетДанных",
Новый Структура("Данные,ПакетДанных", ПакетДанных));
⚠️ Внимание: Недокументированные методы могут измениться в новых версиях платформы, что приведет к ошибкам в работе решения. Всегда тестируйте такие подходы на резервной копии базы данных.
☑️ Оптимизация выгрузки больших данных
6. Типичные ошибки и их решение
При выгрузке запроса в массив разработчики часто сталкиваются с типичными ошибками, которые могут приводить к падению производительности или некорректным результатам. Рассмотрим наиболее распространенные проблемы и способы их решения.
1. Ошибка "Превышен максимальный размер коллекции"
Возникает при попытке выгрузить слишком большой результат (обычно более 100 000 строк). Решение:
- 🛠 Разбить запрос на пакеты с использованием
ПЕРВЫЕ. - 🛠 Использовать временные таблицы для промежуточных результатов.
- 🛠 Выгружать данные не в массив, а непосредственно в файл или базу данных.
2. Несоответствие типов данных
При выгрузке дат, булевых значений или ссылок могут возникать ошибки преобразования. Решение:
- 🛠 Явно указывать типы в запросе (например,
ВЫБРАТЬ ДАТАВРЕМЯ(0) КАК ПустаяДата). - 🛠 Использовать функции преобразования:
Формат(Дата, "ДФ=dd.MM.yyyy"). - 🛠 Обрабатывать данные в цикле с явным приведением типов.
3. Медленная работа запроса
Если запрос выполняется слишком долго, проверьте:
- 🐢 Наличие индексов по полям, используемым в условиях
ГДЕиСГРУППИРОВАТЬ ПО. - 🐢 Отсутствие лишних соединений таблиц (особенно
ПОЛНОЕ СОЕДИНЕНИЕ). - 🐢 Использование функций в условиях (например,
ГДЕ ЛЕВ(Наименование, 3) = "АБВ"— это блокирует использование индексов).
4. Потеря данных при выгрузке
Иногда в результирующем массиве отсутствуют строки или колонки. Причины и решения:
- 🔍 Проверьте, что в запросе нет условий, отсекающих данные (например,
РАЗЛИЧНЫЕилиГДЕ). - 🔍 Убедитесь, что при выгрузке указаны правильные индексы колонок.
- 🔍 Для выборки с
ОБЪЕДИНИТЬпроверьте, что все части запроса возвращают одинаковое количество колонок.
5. Ошибки при работе с транзакциями
Если выгрузка данных происходит в рамках транзакции, могут возникать блокировки или несоответствия данных. Рекомендации:
- 🔄 Выгружайте данные до начала транзакции или используйте отдельные соединения.
- 🔄 Для длинных операций используйте
НачатьТранзакцию()иЗафиксироватьТранзакцию()с явным управлением. - 🔄 Избегайте выгрузки больших данных в транзакциях с высоким уровнем изоляции.
7. Практические примеры для разных задач
Рассмотрим несколько реальных сценариев, в которых требуется выгрузка запроса в массив, и оптимальные способы их решения.
Пример 1: Выгрузка остатков товаров для отчета
Задача: получить остатки товаров на складе в виде массива для дальнейшего формирования Excel-отчета.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование КАК Товар,
| Склад.Наименование КАК Склад,
| ОстаткиТоваров.КоличествоОстатка КАК Количество
|ИЗ
| РегистрНакопления.ОстаткиТоваров.Остатки(&ДатаОтчета,) КАК ОстаткиТоваров
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ОстаткиТоваров.Номенклатура = Номенклатура.Ссылка
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Склады КАК Склад
| ПО ОстаткиТоваров.Склад = Склад.Ссылка
|УПОРЯДОЧИТЬ ПО
| Номенклатура.Наименование,
| Склад.Наименование";
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
МассивОстатков = Запрос.Выполнить().Выгрузить();
Пример 2: Выгрузка истории изменений документа
Задача: получить все изменения документа "Заказ клиента" для анализа.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ИсторияДокумента.ДатаИзменения КАК Дата,
| ИсторияДокумента.Пользователь.Наименование КАК Пользователь,
| ИсторияДокумента.ОписаниеИзменения КАК Описание
|ИЗ
| РегистрСведений.ИсторияИзмененийДокументов КАК ИсторияДокумента
|ГДЕ
| ИсторияДокумента.СсылкаНаДокумент = &СсылкаНаДокумент
|УПОРЯДОЧИТЬ ПО
| ИсторияДокумента.ДатаИзменения УБЫВ";
Запрос.УстановитьПараметр("СсылкаНаДокумент", ДокументСсылка);
МассивИстории = Новый Массив;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
МассивИстории.Добавить(Новый Структура("Дата,Пользователь,Описание",
Выборка.Дата, Выборка.Пользователь, Выборка.Описание));
КонецЦикла;
Пример 3: Выгрузка данных для интеграции с внешней системой
Задача: подготовить данные о клиентах для передачи в CRM-систему в формате JSON.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Клиент.ИНН КАК ИНН,
| Клиент.Наименование КАК Название,
| Клиент.Адрес КАК Адрес,
| Клиент.Телефон КАК Телефон
|ИЗ
| Справочник.Контрагенты КАК Клиент
|ГДЕ
| Клиент.ЭтоГруппа = ЛОЖЬ
| И Клиент.ПометкаУдаления = ЛОЖЬ";
Результат = Запрос.Выполнить();
МассивКлиентов = Новый Массив;
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
КлиентJSON = Новый Структура;
КлиентJSON.Вставить("inn", Выборка.ИНН);
КлиентJSON.Вставить("name", Выборка.Название);
КлиентJSON.Вставить("address", Выборка.Адрес);
КлиентJSON.Вставить("phone", Выборка.Телефон);
МассивКлиентов.Добавить(КлиентJSON);
КонецЦикла;
// Преобразуем в JSON
JSONТекст = СериализоватьJSON(МассивКлиентов);
Пример 4: Выгрузка данных с агрегацией
Задача: получить сводные данные по продажам по месяцам.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВЫРАЗИТЬ(НачалоМесяца(Продажи.Дата) КАК ДАТА) КАК Месяц,
| СУММА(Продажи.СуммаДокумента) КАК СуммаПродаж,
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Продажи.Клиент) КАК КоличествоКлиентов
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Продажи
|ГДЕ
| Продажи.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
|СГРУППИРОВАТЬ ПО
| Месяц
|УПОРЯДОЧИТЬ ПО
| Месяц";
Запрос.УстановитьПараметр("ДатаНачала", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаОкончания", КонецГода(ТекущаяДата()));
МассивПродаж = Запрос.Выполнить().Выгрузить();
FAQ: Частые вопросы по выгрузке запроса в массив
Можно ли выгрузить запрос в массив структур вместо двумерного массива?
Да, для этого нужно вручную обработать результат запроса через цикл Пока Выборка.Следующий() и формировать структуру для каждой строки. Пример:
МассивСтруктур = Новый Массив;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
СтруктураСтроки = Новый Структура;
СтруктураСтроки.Вставить("Поле1", Выборка.Поле1);
СтруктураСтроки.Вставить("Поле2", Выборка.Поле2);
МассивСтруктур.Добавить(СтруктураСтроки);
КонецЦикла;
Такой подход удобен, если нужно работать с именованными полями, а не с индексами колонок.
Как выгрузить запрос в массив, если в результате есть поля с одинаковыми именами?
Если в запросе используются поля с одинаковыми именами (например, при объединении таблиц), платформа автоматически добавляет суффиксы "_1", "_2" и т.д. Чтобы избежать путаницы, явно переименуйте колонки в запросе:
ВЫБРАТЬ
Таблица1.Поле КАК Поле_Таблица1,
Таблица2.Поле КАК Поле_Таблица2
ИЗ ...
Либо обращайтесь к колонкам по индексам при выгрузке: Результат.Выгрузить(0) для первой колонки.