Работа с массивами данных в 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):

ФоновоеЗадание = ФоновыеЗадания.Выполнить("ОбработатьПакетДанных",

Новый Структура("Данные,ПакетДанных", ПакетДанных));

⚠️ Внимание: Недокументированные методы могут измениться в новых версиях платформы, что приведет к ошибкам в работе решения. Всегда тестируйте такие подходы на резервной копии базы данных.

☑️ Оптимизация выгрузки больших данных

Выполнено: 0 / 5

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) для первой колонки.

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