Выгрузка данных запроса в массив: способы и примеры кода
Содержание
Чтобы получить массив из результата запроса, в платформе есть два коротких пути. Нужен массив значений одной колонки — пишите Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка"). Нужна вся таблица или массив структур — выгружайте результат в таблицу значений либо собирайте массив в цикле обхода выборки.
Метод Выгрузить() сам по себе возвращает не массив, а ТаблицаЗначений — на этом спотыкаются чаще всего. Массив из неё достаёт уже ВыгрузитьКолонку(). Ниже — пять рабочих приёмов: от одной строки кода до пакетных запросов с временными таблицами для больших выборок. Весь код на встроенном языке 1С 8.3, примеры решают задачу целиком.
Одна колонка в массив: Выгрузить и ВыгрузитьКолонку
Самый частый случай — собрать массив ссылок или значений одного поля. Два вызова подряд:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления";
МассивСсылок = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
Что здесь происходит по шагам:
Выполнить()возвращает объектРезультатЗапроса.Выгрузить()превращает его вТаблицаЗначений.ВыгрузитьКолонку("Ссылка")отдаёт готовыйМассивзначений этой колонки.
ВыгрузитьКолонку() есть у таблицы значений, но НЕ у результата запроса. Если вызвать его прямо на РезультатЗапроса, платформа выдаст «Поле объекта не обнаружено». Поэтому .Выгрузить() в цепочке пропускать нельзя.
Колонку можно указать не только именем, но и индексом — ВыгрузитьКолонку(0). Это удобно, когда имя поля заранее неизвестно, а нужна первая колонка.
У самого Выгрузить() есть параметр обхода результата. Для плоского набора строк оставляйте значение по умолчанию (прямой обход). Он нужен только для запросов с итогами — об этом в разделе о группировках ниже.
Если дальше массив идёт только в параметр другого запроса, выбирайте в тексте ровно одно поле — Ссылка. Лишние колонки замедляют выгрузку и занимают память впустую.
Сбор массива в цикле обхода выборки
Когда нужен массив структур или собственных объектов, а не плоский список значений, таблицу значений обходить неудобно. Берём выборку и наполняем массив вручную:
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
МассивСтрок = Новый Массив;
Пока Выборка.Следующий() Цикл
Строка = Новый Структура("Номенклатура, Количество");
Строка.Номенклатура = Выборка.Номенклатура;
Строка.Количество = Выборка.Количество;
МассивСтрок.Добавить(Строка);
КонецЦикла;
Выбрать() возвращает ВыборкаИзРезультатаЗапроса. Её нельзя перебирать по индексу как массив — только последовательно через Следующий(), который двигает курсор и возвращает Ложь, когда строки кончились.
Этот способ выбирайте, если:
- 🔧 к каждой строке применяется логика — расчёт, проверка, преобразование;
- 📦 в массив кладёте структуры с несколькими полями;
- ⏭ часть строк по условию нужно пропустить (
Продолжитьвнутри цикла).
Проверьте пустоту до цикла — Если Результат.Пустой() Тогда Возврат; КонецЕсли;. Пустой результат не ошибка, но дальше по коду обращение к пустому массиву часто и приводит к сбою.
Если нужен массив только ссылок, структуру заводить ни к чему — добавляйте значение напрямую: МассивСсылок.Добавить(Выборка.Ссылка). Цикл оправдан лишь тогда, когда внутри есть логика; для чистой выгрузки одной колонки ВыгрузитьКолонку() короче и быстрее.
Выборочная выгрузка: фильтр и нужные поля
«Выборочная» выгрузка — это когда в массив попадают не все данные, а отобранные. Правильнее отбирать их на стороне запроса, а не в коде 1С:
- условие
ГДЕотсекает лишние строки до выгрузки; РАЗЛИЧНЫЕубирает дубли, если нужен массив уникальных значений;УПОРЯДОЧИТЬ ПОзадаёт порядок элементов в массиве.
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Продажи.Контрагент КАК Контрагент
|ИЗ
| РегистрНакопления.Продажи КАК Продажи
|ГДЕ
| Продажи.Период МЕЖДУ &Начало И &Конец
|УПОРЯДОЧИТЬ ПО
| Контрагент";
Запрос.УстановитьПараметр("Начало", НачалоПериода);
Запрос.УстановитьПараметр("Конец", КонецПериода);
МассивКонтрагентов = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Контрагент");
Фильтровать уже собранный массив перебором — лишняя работа: сервер СУБД делает отбор быстрее и возвращает меньше данных по сети. Отбирайте в запросе, а не в цикле.
Если всё же нужно собрать массив по условию в коде (например, значение зависит от результата функции), используйте обход выборки из предыдущего раздела и Продолжить для пропуска строк.
Большие выборки: временные таблицы и пакет
Выгружать в массив десятки и сотни тысяч записей опасно — всё держится в оперативной памяти сервера. Для объёмных данных применяют временные таблицы и пакетный запрос.
Временная таблица собирает промежуточный набор, а следующие запросы пакета обращаются к ней без повторного чтения основных таблиц:
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.КоличествоОстаток КАК Остаток
|ПОМЕСТИТЬ ВТОстатки
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
|;
|ВЫБРАТЬ
| ВТОстатки.Номенклатура КАК Номенклатура
|ИЗ
| ВТОстатки КАК ВТОстатки
|ГДЕ
| ВТОстатки.Остаток > 0";
Пакет = Запрос.ВыполнитьПакет();
МассивНоменклатуры = Пакет[1].Выгрузить().ВыгрузитьКолонку("Номенклатура");
ВыполнитьПакет() возвращает массив объектов РезультатЗапроса — по одному на каждый запрос, разделённый ;. Обращаемся к нужному по индексу: Пакет[1] — это второй запрос пакета (нумерация с нуля).
Если результат измеряется сотнями тысяч строк, подумайте, нужен ли вообще массив целиком. Часто данные можно обработать прямо в цикле выборки, не накапливая их в памяти. Массив на миллион элементов — это риск нехватки памяти рабочего процесса.
Приёмы ускорения выгрузки
Несколько тонкостей, которые заметно экономят время на реальных данных.
Индекс на таблице значений. Если по выгруженной таблице предстоит многократный поиск через Найти или НайтиСтроки, добавьте индекс — поиск пойдёт не перебором:
ТЗ = Запрос.Выполнить().Выгрузить();
ТЗ.Индексы.Добавить("Номенклатура");
НайденныеСтроки = ТЗ.НайтиСтроки(Новый Структура("Номенклатура", ТекущаяНоменклатура));
Дедупликация на уровне запроса. РАЗЛИЧНЫЕ или группировка СГРУППИРОВАТЬ ПО уберут повторы до выгрузки — массив сразу получится уникальным, без последующей чистки.
Только нужные колонки. Чем меньше полей в ВЫБРАТЬ, тем меньше данных едет с сервера СУБД и тем быстрее Выгрузить(). Не тащите * и справочные реквизиты, которые не используете.
Прямая обработка вместо массива. Если данные нужны один раз и по порядку, обходите выборку и обрабатывайте каждую строку на месте. Промежуточный массив здесь — лишняя память и лишний проход.
Массив в параметр запроса и обратно
Типовая задача: есть массив ссылок, нужно получить по ним данные. Массив передаётся в параметр, а в тексте запроса используется конструкция В (&Параметр):
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Номенклатура КАК Номенклатура,
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены
|ГДЕ
| Цены.Номенклатура В (&СписокНоменклатуры)";
Запрос.УстановитьПараметр("СписокНоменклатуры", МассивНоменклатуры);
ТаблицаЦен = Запрос.Выполнить().Выгрузить();
Так замыкается полный круг: из первого запроса собрали массив ссылок, передали его параметром во второй, получили по ним цены. Это основной сценарий, ради которого массив из запроса обычно и выгружают.
Обратите внимание на тип значений в массиве: если в В (&Список) передать массив строк там, где поле хранит ссылки, отбор просто ничего не найдёт — ошибки не будет, но и результат окажется пустым.
Есть и предел разумного размера. Когда в массиве тысячи элементов, отбор В (&Список) начинает тормозить: условие разворачивается в длинный список значений для СУБД. В таком случае выгоднее поместить массив во временную таблицу и соединить её с основной по полю — платформа отработает это как соединение, а не как перечисление:
Запрос.УстановитьПараметр("Список", МассивНоменклатуры);
Запрос.Текст =
"ВЫБРАТЬ Ссылка ПОМЕСТИТЬ ВТСписок ИЗ &Список КАК Список
|;
|ВЫБРАТЬ Цены.Номенклатура, Цены.Цена
|ИЗ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТСписок КАК ВТСписок
| ПО Цены.Номенклатура = ВТСписок.Ссылка";
Для коротких списков из десятков значений разница незаметна — там проще обычное В (&Список).
Сравнение способов: что когда применять
Короткая сводка, чтобы выбрать инструмент под конкретную задачу, не перебирая весь текст.
| Способ | Что возвращает | Когда применять |
|---|---|---|
Выгрузить().ВыгрузитьКолонку() | массив значений одной колонки | нужен список ссылок или значений одного поля |
Выгрузить() | ТаблицаЗначений целиком | нужны все поля, поиск и фильтрация в коде |
Обход Выбрать() в цикле | любой — массив структур, объектов | к каждой строке применяется логика |
Временные таблицы + ВыполнитьПакет() | результат одного из запросов пакета | большие объёмы, многоступенчатая обработка |
Правило по памяти простое: материализуйте массив только тогда, когда данные нужны повторно или целиком. Если проходите по строкам один раз и по порядку — обходите выборку без накопления.
Для запроса с итогами массив значений группировки достают так: Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам).ВыгрузитьКолонку("Поле"). Прямой обход в этом случае вернул бы и строки детальных записей.
Выполнить().Выгрузить().ВыгрузитьКолонку("Поле") — одна строка, без цикла.Выбрать() и Пока Выборка.Следующий() Цикл с Массив.Добавить().Выгрузить() в ТаблицаЗначений, при частом поиске — добавьте индекс.ВыполнитьПакет(); по возможности не накапливайте массив целиком.Ошибки и их причины
Четыре сбоя, на которые уходит больше всего времени.
ВыгрузитьКолонку() вызван на результате запроса. Сначала .Выгрузить() в таблицу значений, потом уже выгрузка колонки.Результат.Пустой() и параметры отбора), либо забыт Выборка.Следующий() и цикл не отработал.РАЗЛИЧНЫЕ или группировки. Убирайте повторы на стороне запроса, а не перебором массива.Частые вопросы
Чем отличается Выгрузить() от Выбрать()?
Выгрузить() сразу отдаёт всю таблицу значений в память. Выбрать() возвращает курсор, который читает строки по одной через Следующий() — экономнее по памяти на больших выборках.Можно ли получить массив сразу, без таблицы значений?
ВыгрузитьКолонку() — да, для одной колонки это одна строка кода. Для массива структур всё равно нужен обход выборки или таблицы.Как выгрузить результат с группировками (итогами)?
Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам). Для простого плоского набора оставляйте значение по умолчанию — прямой обход.Почему массив из запроса лучше, чем выборка через точку в цикле?
В (&Массив) читает все нужные данные разом. Обращение к реквизитам объекта в цикле порождает отдельный запрос на каждую строку — это и есть классическая «проблема N+1».Сохраняется ли порядок строк в выгруженном массиве?
УПОРЯДОЧИТЬ ПО — без него СУБД вправе вернуть строки в любом порядке.