К статье
Практический материал

Разработка и Конфигурация · 14 мин

Как загрузить результат запроса в таблицу значений в 1С

Как загрузить результат запроса в таблицу значений в 1С: метод Выгрузить(), обход выборки, разница с деревом значений, типовые ошибки и примеры кода.

Задача выгрузить результат запроса в таблицу значений встречается в 1С постоянно: при заполнении табличных частей, подготовке данных для отчётов, обмене между процедурами и построении произвольных форм. Встроенный язык даёт для этого штатный инструмент — метод Выгрузить() объекта «РезультатЗапроса», который одной строкой превращает выборку в полноценную таблицу значений.

Однако на практике разработчики сталкиваются с нюансами: какие типы колонок получит таблица, чем выгрузка отличается от обхода выборки в цикле, когда нужен параметр ОбходРезультатаЗапроса.ПоГруппировкам и почему иногда вместо таблицы значений правильнее использовать дерево значений. Ниже разобраны рабочие способы, ограничения и типовые ошибки с примерами кода, которые можно переносить в конфигурацию без доработки.

Базовый способ: метод Выгрузить() результата запроса

Стандартная цепочка выглядит так: создаётся объект «Запрос», задаётся текст, при необходимости устанавливаются параметры, запрос выполняется, и результат выгружается в таблицу значений. Метод Выполнить() возвращает объект «РезультатЗапроса», у которого есть два ключевых метода: Выгрузить() — для получения таблицы значений и Выбрать() — для получения выборки с построчным обходом.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Номенклатура,

| Номенклатура.Код КАК Код,

| Номенклатура.Наименование КАК Наименование

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ГДЕ

| НЕ Номенклатура.ПометкаУдаления";

ТаблицаРезультат = Запрос.Выполнить().Выгрузить();

После выполнения переменная ТаблицаРезультат содержит объект «ТаблицаЗначений» с тремя колонками — Номенклатура, Код, Наименование. Имена колонок берутся из псевдонимов полей запроса (после ключевого слова КАК). Если псевдоним не задан, платформа сформирует имя автоматически, и обращаться к такой колонке будет неудобно — поэтому псевдонимы стоит задавать всегда.

Типы колонок таблица значений наследует из типов полей запроса: ссылочные поля останутся ссылками, строки — строками с сохранением длины, числа — числами с заданной точностью. Это важное преимущество перед ручным созданием таблицы через Новый ТаблицаЗначений — не нужно описывать колонки вручную и следить за соответствием типов.

Выгрузка с параметрами и временными таблицами

В реальных задачах запрос почти всегда содержит параметры — отборы по периоду, организации, списку значений. Параметры устанавливаются методом УстановитьПараметр() до выполнения запроса. Если параметр не установлен, при выполнении возникнет ошибка, и таблица значений не будет получена.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Продажи.Номенклатура КАК Номенклатура,

| СУММА(Продажи.Количество) КАК Количество,

| СУММА(Продажи.Сумма) КАК Сумма

|ИЗ

| РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи

|СГРУППИРОВАТЬ ПО

| Продажи.Номенклатура";

Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДатаСеанса()));

Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДатаСеанса()));

ТЗ = Запрос.Выполнить().Выгрузить();

Если в качестве параметра передаётся массив или список значений для условия В (&СписокНоменклатуры), выгрузка работает так же — платформа сама разворачивает коллекцию в условие. Удобный приём: передать в запрос уже готовую таблицу значений как параметр, поместить её во временную таблицу и соединить с другими данными, а итог снова выгрузить в таблицу значений. Для этого понадобится менеджер временных таблиц.

Запрос = Новый Запрос;

Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

Запрос.Текст =

"ВЫБРАТЬ

| Исходные.Номенклатура КАК Номенклатура,

| Исходные.Цена КАК Цена

|ПОМЕСТИТЬ ВТ_Цены

|ИЗ

| &ИсходныеДанные КАК Исходные

|;

|ВЫБРАТЬ

| ВТ_Цены.Номенклатура КАК Номенклатура,

| ВТ_Цены.Цена КАК Цена,

| Ном.Артикул КАК Артикул

|ИЗ

| ВТ_Цены КАК ВТ_Цены

| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ном

| ПО ВТ_Цены.Номенклатура = Ном.Ссылка";

Запрос.УстановитьПараметр("ИсходныеДанные", ИсходнаяТЗ);

РезультатТЗ = Запрос.Выполнить().Выгрузить();

Выгрузка по группировкам: когда получится дерево значений

Если запрос содержит итоги (конструкция ИТОГИ ПО), метод Выгрузить() по умолчанию всё равно вернёт плоскую таблицу значений, включающую и строки итогов, и строки детальных записей. Но если передать в метод параметр ОбходРезультатаЗапроса.ПоГруппировкам, результатом будет уже не таблица, а дерево значений — иерархическая структура, где итоговые строки содержат вложенные строки деталей.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Продажи.Контрагент КАК Контрагент,

| Продажи.Номенклатура КАК Номенклатура,

| Продажи.КоличествоОборот КАК Количество

|ИЗ

| РегистрНакопления.Продажи.Обороты КАК Продажи

|ИТОГИ

| СУММА(Количество)

|ПО

| Контрагент";

ДеревоРезультат = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);

Типичная ошибка — присвоить такой результат переменной и дальше обрабатывать её как таблицу значений. У дерева значений строки доступны через коллекцию Строки, а не напрямую, и методы вроде Найти() работают только по первому уровню иерархии. Если иерархия не нужна, не передавайте параметр обхода и не используйте ИТОГИ без необходимости — плоская таблица проще в обработке.

Сравнение вариантов

Выгрузить() без параметров

Плоская таблица значений. Подходит для большинства задач: заполнение табличных частей, обмен данными, выгрузка в Excel. Строки итогов включаются как обычные строки, если в запросе есть ИТОГИ.

Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам)

Возвращает дерево значений с иерархией по группировкам. Нужен для древовидных форм и многоуровневых отчётов. Требует обхода через коллекцию Строки.

Выбрать() с обходом в цикле

Не создаёт таблицу значений, а читает данные построчно. Экономнее по памяти на больших выборках, но результат нельзя передать между вызовами или отсортировать после получения.

Выгрузка через выборку: когда таблицу строят вручную

Иногда прямой выгрузки недостаточно: нужно добавить вычисляемые колонки, отфильтровать строки по сложному условию или преобразовать значения на лету. В этом случае используют выборку — метод Выбрать() — и заполняют таблицу значений в цикле. Таблицу можно либо создать с нуля, либо взять готовую структуру из результата запроса и очищать её.

Результат = Запрос.Выполнить();

ТЗ = Результат.Выгрузить();

ТЗ.Колонки.Добавить("СуммаСНДС", Новый ОписаниеТипов("Число", , , Новый КвалификаторыЧисла(15, 2)));

Выборка = Результат.Выбрать();

Индекс = 0;

Пока Выборка.Следующий() Цикл

СтрокаТЗ = ТЗ[Индекс];

СтрокаТЗ.СуммаСНДС = Выборка.Сумма * 1.2;

Индекс = Индекс + 1;

КонецЦикла;

Обратите внимание: выборка и выгруженная таблица связаны только порядком строк. Пока вы не меняли состав и порядок записей, строка выборки по счёту соответствует строке таблицы с тем же индексом — это позволяет дополнять таблицу вычисляемыми значениями без поиска по ключу. Но если между выгрузкой и обходом выборки таблица была отсортирована или из неё удалены строки, соответствие нарушится, и данные «поедут».

Альтернатива без выгрузки — создать пустую таблицу значений, описать колонки вручную и добавлять строки через ТЗ.Добавить() с заполнением из полей выборки. Этот путь длиннее и подвержен ошибкам в типах колонок, поэтому оправдан только тогда, когда состав колонок принципиально отличается от полей запроса.

Порядок действий
  1. 1
    Создайте объект Запрос и задайте текст с псевдонимами полей (КАК ИмяКолонки)
  2. 2
    Установите все параметры через УстановитьПараметр()
  3. 3
    Выполните запрос и выгрузите результат: ТЗ = Запрос.Выполнить().Выгрузить()
  4. 4
    Проверьте результат: ТЗ.Количество() больше нуля, колонки присутствуют в ТЗ.Колонки
  5. 5
    При необходимости добавьте вычисляемые колонки и заполните их в цикле по строкам таблицы

Проверка результата и работа с пустым запросом

Прежде чем обрабатывать таблицу, убедитесь, что запрос вернул данные. У результата запроса есть метод Пустой(), но если вы выгрузили таблицу сразу в цепочке, проверяйте количество строк самой таблицы: Если ТЗ.Количество() = 0 Тогда ... КонецЕсли;. Важно понимать: пустой результат — это не ошибка, выгрузка всё равно вернёт корректную таблицу значений с полным набором колонок, но без строк.

Проверка наличия колонок актуальна, когда текст запроса формируется динамически. Обратиться к несуществующей колонке через СтрокаТЗ.ИмяКолонки вызовет ошибку «Поле объекта не обнаружено». Безопасный способ — предварительно искать колонку: Если ТЗ.Колонки.Найти("Сумма") <> Неопределено Тогда ....

Контрольные точки

Как проверить результат

Количество строк
Норма

ТЗ.Количество() соответствует ожидаемому числу записей выборки

Тревожный признак

Ноль строк при заведомо существующих данных — проверяйте параметры и отборы запроса

Состав колонок
Норма

Все псевдонимы из запроса присутствуют в ТЗ.Колонки

Тревожный признак

Колонка отсутствует или названа автоматически — в запросе не задан псевдоним КАК

Типы значений
Норма

Ссылочные поля содержат ссылки, числа — числа, а не строки

Тревожный признак

Значения стали строками — в запросе применено представление или объединение несовместимых типов

Структура при итогах
Норма

Без параметра обхода — плоская таблица, с ПоГруппировкам — дерево

Тревожный признак

Ожидалась таблица, получено дерево: убран параметр обхода или наоборот

Типичные ошибки и ограничения

Самая частая проблема — потеря типов при объединении запросов. Если в ОБЪЕДИНИТЬ ВСЕ участвуют поля разных типов, итоговая колонка получит составной тип, а при дальнейшей выгрузке значения могут приводиться не так, как ожидает разработчик. Проверяйте типы через ТЗ.Колонки.ИмяКолонки.ТипЗначения при отладке.

Вторая группа ошибок связана с памятью. Выгрузка результата запроса материализует весь набор данных в оперативной памяти сервера. Если запрос возвращает сотни тысяч строк с десятками колонок, таблица значений может занять значительный объём памяти и замедлить работу сеанса. Для больших выборок, которые обрабатываются один раз последовательно, предпочтительнее обход выборки через Выбрать() без промежуточной таблицы.

Третья группа — неочевидное поведение с итогами. Если в запросе есть ИТОГИ, а выгрузка выполняется без параметра обхода, итоговые строки попадут в таблицу как обычные записи с пустыми или NULL-значениями в детальных полях. При последующей обработке их легко принять за реальные данные и, например, задвоить суммы. Либо убирайте ИТОГИ из запроса, либо фильтруйте такие строки, либо используйте выгрузку по группировкам.

Типичные ошибки
01
Не заданы псевдонимы полей — колонки получают автоматические имена, и обращение к ним по имени приводит к ошибке «Поле объекта не обнаружено»
02
Обработка дерева значений как таблицы: методы Найти() и Сортировать() работают только по верхнему уровню, данные нижних уровней теряются из виду
03
Выгрузка огромной выборки в таблицу значений без необходимости — расход памяти сервера; для однократного последовательного чтения достаточно Выбрать()
04
Итоговые строки из запроса с ИТОГИ обрабатываются как детальные данные — итоговые суммы задваиваются
05
Дополнение таблицы через выборку после сортировки или удаления строк — соответствие по индексу нарушается, значения записываются не в те строки

Практические сценарии применения

Заполнение табличной части документа

Классическая задача — заполнить табличную часть данными по отбору. Таблица значений здесь выступает промежуточным буфером: её можно отсортировать, свернуть (метод Свернуть() с группировкой и суммированием) и только потом загрузить в табличную часть методом Загрузить(). Имена колонок таблицы значений должны совпадать с именами реквизитов табличной части — лишние колонки будут проигнорированы, а отсутствующие останутся пустыми.

ТЗ = Запрос.Выполнить().Выгрузить();

ТЗ.Свернуть("Номенклатура", "Количество, Сумма");

ДокументОбъект.Товары.Загрузить(ТЗ);

Передача данных между сервером и клиентом

В управляемых формах таблица значений, полученная на сервере, не передаётся на клиент напрямую — её помещают в реквизит формы типа «ДинамическийСписок»-подобную таблицу или в таблицу значений формы через ЗначениеВДанныеФормы(). Если нужно просто показать произвольные данные в форме, добавьте реквизит формы типа «ТаблицаЗначений» с нужными колонками и загрузите в него результат запроса.

Выгрузка во внешние форматы

Таблица значений — удобный источник для выгрузки в табличный документ (и далее в Excel): цикл по строкам с выводом в область табличного документа. Предварительная выгрузка результата запроса позволяет отсортировать и сгруппировать данные средствами встроенного языка, не переписывая запрос.

Если таблица значений нужна только чтобы один раз пройти по строкам и что-то посчитать, подумайте, нельзя ли выполнить группировку и суммирование прямо в запросе. Агрегация на стороне СУБД почти всегда быстрее, чем выгрузка всех строк и их обработка в цикле встроенного языка.
ПЗ
Практическое замечание

Частые вопросы по выгрузке результата запроса

Чем Выгрузить() отличается от Выбрать()?

Выгрузить() возвращает таблицу значений — весь набор данных сразу помещается в память, его можно сортировать, искать, сворачивать и передавать между процедурами. Выбрать() возвращает выборку для последовательного построчного обхода: памяти требуется меньше, но данные доступны только один раз и только в прямом порядке.

Почему после Выгрузить() таблица пустая, хотя данные в базе есть?

Чаще всего причина в параметрах отбора: не тот период, лишнее условие в ГДЕ или параметр-коллекция, переданный пустым. Проверьте запрос в консоли запросов с теми же параметрами. Вторая возможная причина — виртуальная таблица регистра с отбором, который не соответствует реальным движениям.

Можно ли выгрузить результат запроса в существующую таблицу значений?

Напрямую нет — Выгрузить() всегда создаёт новую таблицу. Если нужно добавить данные в уже существующую таблицу, обойдите выгруженную таблицу в цикле и добавляйте строки, либо используйте метод ВставитьКолонку/ЗагрузитьКолонку для переноса отдельных колонок при совпадении числа строк.

Как сохранить порядок строк из запроса в таблице значений?

Добавьте в запрос предложение УПОРЯДОЧИТЬ ПО — выгрузка сохраняет порядок записей результата. Без УПОРЯДОЧИТЬ ПО порядок строк не гарантирован и может отличаться между выполнениями. Сортировка таблицы значений после выгрузки методом Сортировать() тоже работает, но выполняется уже на стороне встроенного языка.

Что делать, если колонка получила составной тип и значения приводятся некорректно?

Приведите тип на уровне запроса функцией ВЫРАЗИТЬ(Поле КАК Число(15,2)) или ВЫРАЗИТЬ(Поле КАК Строка(100)). Тогда таблица значений получит колонку однозначного типа. Исправлять тип уже в готовой таблице сложнее — придётся создавать новую колонку и переносить значения.

Есть ли ограничение на количество строк при выгрузке?

Жёсткого лимита платформа не устанавливает, ограничением служит доступная оперативная память сервера или клиента. На практике таблицы в сотни тысяч строк с несколькими колонками работают приемлемо, но миллионы строк уже требуют пересмотра подхода — пакетной обработки или обхода выборки без материализации.

Итог

Что важно запомнить

Результат запроса загружается в таблицу значений одной строкой: ТЗ = Запрос.Выполнить().Выгрузить(). Задавайте псевдонимы полей, проверяйте количество строк и состав колонок, не используйте ИТОГИ без необходимости, а для больших однократных выборок предпочитайте обход через Выбрать(). Для иерархических данных применяйте Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам), помня, что вернётся дерево значений.