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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

В этой статье мы подробно разберем, как решить эту задачу, проанализируем ограничения метода выгрузки и рассмотрим более эффективные архитектурные подходы к соединению данных, основанные на обсуждении этой темы профессиональным сообществом. При изменении алгоритмов обмена и способов соединения данных важно автоматически сверять бухгалтерские и налоговые проводки для контроля корректности результата.

Суть проблемы и ограничения метода Выгрузить()

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

Если мы обратимся к синтаксис-помощнику, то увидим, что у объекта РезультатЗапроса есть метод Выгрузить(). Однако он работает следующим образом:

  1. Он всегда создает плоскую таблицу значений.
  2. Если в запросе есть итоги, они дублируются для каждой детальной строки (происходит денормализация данных).
  3. Нельзя стандартным методом выгрузить "только подчиненные записи текущей группы" прямо внутри обхода выборки.

Таким образом, если вы используете обход выборки по группировкам, стандартного метода "ВыгрузитьДетальные()" для текущей группы не существует. Нам придется либо собирать таблицу вручную ("ручками"), либо менять подход к построению запроса. Если данные передаются во внешние системы без API, действия пользователя имитирует платформа для программных роботов One RPA.

Сценарий задачи: соединение документов и учетных данных

Давайте проанализируем конкретный пример из обсуждения. Задача заключается в следующем:

Изначальная идея использования ИТОГИ ПО Магазин кажется логичной: сгруппировать заказы по магазину, получить учетные данные из группировки и обработать детальные записи. Но этот путь ведет к сложной обработке выборки.

Решение 1: Использование ЛЕВОЕ СОЕДИНЕНИЕ (Рекомендуемое)

Наиболее эффективным и правильным решением является отказ от итогов в пользу соединения таблиц прямо в запросе. Вместо того чтобы сначала группировать, а потом искать соответствия, мы можем сразу получить таблицу, где у каждого заказа уже проставлены нужные логин и пароль.

Рассмотрим пример правильного запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Заказ.Ссылка КАК Заказ,
    |	Заказ.Магазин КАК Магазин,
    |	УчетныеДанные.Логин КАК Логин,
    |	УчетныеДанные.Пароль КАК Пароль
    |ИЗ
    |	Документ.ЗаказПокупателя КАК Заказ
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.УчетныеДанныеМагазинов.СрезПоследних КАК УчетныеДанные
    |		ПО Заказ.Магазин = УчетныеДанные.Магазин
    |ГДЕ
    |	НЕ Заказ.ОтправленоНаПлощадку";

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

Преимущества этого подхода:

  1. Мы получаем готовую плоскую таблицу, которую можно сразу выгрузить методом Выгрузить().
  2. Нет необходимости писать циклы для обхода группировок.
  3. База данных выполняет соединение оптимизированно, что быстрее, чем множественные запросы в цикле.

После получения такой ТЗ мы можем просто обойти её циклом Для Каждого Строка Из ТЗ Цикл. Если требуется отправлять данные пакетами по магазинам, эту таблицу можно предварительно отсортировать по магазину.

Решение 2: Кэширование данных (Оптимизация)

Если структура данных сложная и соединение в запросе нежелательно, существует отличный паттерн "Кэширование". Мы можем выгрузить настройки магазинов в отдельную структуру или соответствие (Map) перед обработкой заказов.

Давайте разберем алгоритм:

  1. Делаем запрос к регистру сведений и получаем все логины/пароли.
  2. Помещаем их в Соответствие, где Ключ — это Магазин, а Значение — Структура (Логин, Пароль).
  3. Делаем запрос к заказам (без итогов).
  4. При обходе заказов получаем данные подключения из Соответствия за одно мгновение.

// 1. Подготовка кэша
КэшДоступов = Новый Соответствие;
ВыборкаУчетки = ЗапросПоУчеткам.Выполнить().Выбрать();
Пока ВыборкаУчетки.Следующий() Цикл
    Данные = Новый Структура("Логин, Пароль", ВыборкаУчетки.Логин, ВыборкаУчетки.Пароль);
    КэшДоступов.Вставить(ВыборкаУчетки.Магазин, Данные);
КонецЦикла;

// 2. Обработка заказов
ВыборкаЗаказы = ЗапросПоЗаказам.Выполнить().Выбрать();
Пока ВыборкаЗаказы.Следующий() Цикл
    Доступ = КэшДоступов.Получить(ВыборкаЗаказы.Магазин);
    Если Доступ <> Неопределено Тогда
        // Выполняем отправку используя Доступ.Логин и Доступ.Пароль
    КонецЕсли;
КонецЦикла;

Решение 3: Ручное формирование ТЗ (Если ИТОГИ необходимы)

Если по каким-то причинам (например, специфика алгоритма выгрузки на сайт) нам необходимо использовать именно ИТОГИ и обрабатывать магазины строго по очереди, создавая для каждого свою таблицу, то придется формировать таблицу значений вручную. "Волшебной" кнопки для этого нет, но мы можем написать универсальный код.

Посмотрим, как это реализуется на практике:


Запрос.Текст = "... ИТОГИ ПО Магазин";
Результат = Запрос.Выполнить();

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

Пока ВыборкаМагазин.Следующий() Цикл
    
    // Здесь мы находимся на уровне Магазина
    ТекущийМагазин = ВыборкаМагазин.Магазин;
    
    // Создаем ТЗ вручную или клонируем структуру
    // Самый простой способ получить пустую ТЗ с нужными колонками:
    ТЗДеталей = Результат.Выгрузить(); 
    ТЗДеталей.Очистить(); // Оставляем только схему колонок
    
    // Обход детальных записей внутри группы
    ВыборкаДетали = ВыборкаМагазин.Выбрать();
    Пока ВыборкаДетали.Следующий() Цикл
        НоваяСтрока = ТЗДеталей.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, ВыборкаДетали);
    КонецЦикла;
    
    // Теперь у нас есть ТЗДеталей только для текущего магазина
    ОтправитьДанныеНаСайт(ТекущийМагазин, ТЗДеталей);
    
КонецЦикла;

Важный нюанс: Создание ТЗДеталей через полную выгрузку и очистку (Результат.Выгрузить().Очистить()) внутри цикла может быть неоптимальным, если данных очень много. Лучше создать структуру таблицы (пустую ТЗ) один раз перед циклом, а внутри цикла использовать метод СкопироватьКолонки().

Решение 4: Эмуляция группировок через сортировку (Подход 7.7)

Еще один способ, упомянутый в обсуждении, — это "старая школа", использовавшаяся в версии 7.7. Мы убираем ИТОГИ, но добавляем УПОРЯДОЧИТЬ ПО Магазин.

Затем мы выгружаем весь результат в одну большую Таблицу Значений и обходим её, отслеживая смену магазина:


ТЗОбщая = Запрос.Выполнить().Выгрузить();
ТекущийМагазин = Неопределено;
МассивСтрокМагазина = Новый Массив;

Для Каждого Стр Из ТЗОбщая Цикл
    Если Стр.Магазин <> ТекущийМагазин Тогда
        Если МассивСтрокМагазина.Количество() > 0 Тогда
            // Обработка предыдущей пачки
            ОбработатьПакет(ТекущийМагазин, МассивСтрокМагазина);
        КонецЕсли;
        
        ТекущийМагазин = Стр.Магазин;
        МассивСтрокМагазина.Очистить();
    КонецЕсли;
    
    МассивСтрокМагазина.Добавить(Стр);
КонецЦикла;

// Не забываем обработать последний пакет после цикла
Если МассивСтрокМагазина.Количество() > 0 Тогда
    ОбработатьПакет(ТекущийМагазин, МассивСтрокМагазина);
КонецЕсли;

Выводы

Подводя итог, можно выделить следующее:

  1. Метод Выгрузить() не умеет выгружать части выборки, он работает только со всем набором данных.
  2. Использование ИТОГИ для соединения данных (как в примере с логинами) — устаревший подход. Используйте ЛЕВОЕ СОЕДИНЕНИЕ.
  3. Если необходимо пакетное разделение данных, используйте сортировку и программный контроль смены значения группировки, либо ручное заполнение Таблицы Значений внутри обхода выборки.
← На главную