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