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

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

В разработке на платформе 1С:Предприятие часто возникает задача получения иерархических структур данных. Классический пример — запрос к документу, где мы можем одной строкой получить ссылку на сам документ и вложенную в ячейку таблицу его товаров. Это выглядит удобно и лаконично:


ВЫБРАТЬ
    ПоступлениеТоваровНаСклад.Ссылка КАК Ссылка,
    ПоступлениеТоваровНаСклад.Товары.(
        Номенклатура КАК Номенклатура,
        Количество КАК Количество
    ) КАК Товары
ИЗ
    Документ.ПоступлениеТоваровНаСклад КАК ПоступлениеТоваровНаСклад

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

Почему это не работает для произвольных таблиц

Проанализируем ситуацию: синтаксис Поле.(ВложенноеПоле1, ВложенноеПоле2) жестко зашит в парсере языка запросов 1С. Этот механизм работает исключительно для объектов, имеющих тип Табличная часть. Платформа на уровне СУБД выполняет оптимизированное соединение или несколько запросов, самостоятельно упаковывая результат в объект типа РезультатЗапроса внутри ячейки конкретной строки.

Для временных таблиц, созданных внутри запроса, или для результатов левого соединения двух обычных таблиц, типа «Таблица значений» внутри ячейки в языке запросов не существует. Поэтому нам приходится использовать альтернативные методы формирования иерархии.

Решение 1: Обход по двум упорядоченным курсорам (самый быстрый метод)

Если нашей целью является максимальная производительность (например, формирование остатков по складам для HTTP-сервиса), рассмотрим метод «плоской» выборки с использованием двух указателей. Основная идея заключается в том, чтобы получить два набора данных, упорядоченных по одному и тому же ключу (например, по Номенклатуре), и обходить их синхронно.

Разберем реализацию этого метода по шагам:

  1. Подготовим первый запрос для получения «шапок» (например, список товаров и их общий остаток).
  2. Подготовим второй запрос для получения «деталей» (например, разбивка этих товаров по складам).
  3. Обязательно установим УПОРЯДОЧИТЬ ПО для обоих запросов по ключевому полю.
  4. Запустим цикл, который будет сопоставлять данные.

Посмотрим на пример кода:


// Предположим, у нас есть два результата запроса: РезультатыЗапросаОстатки и РезультатыЗапросаДетально
ВыборкаШапка = РезультатыЗапросаОстатки.Выбрать();
ВыборкаДетали = РезультатыЗапросаДетально.Выбрать();

// Позиционируем детальную выборку на первую строку заранее
Если ВыборкаДетали.Следующий() Тогда
    ЕстьДанныеДеталей = Истина;
Иначе
    ЕстьДанныеДеталей Ложь;
КонецЕсли;

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

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

Решение 2: Использование итогов и обход по группировкам

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

Рассмотрим пример запроса:


ВЫБРАТЬ
    Остатки.Номенклатура КАК Номенклатура,
    Остатки.Склад КАК Склад,
    Остатки.ВНаличии КАК Остаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ИТОГИ
    СУММА(Остаток)
ПО
    Номенклатура

Теперь разберем, как программно получить вложенные таблицы для каждой номенклатуры:


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

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

Этот способ удобен тем, что 1С сама берет на себя сопоставление данных, но он чуть медленнее предыдущего из-за накладных расходов на создание объектов выборки и выгрузку таблиц.

Решение 3: Система компоновки данных (СКД)

Система компоновки данных умеет работать со «Вложенными наборами данных». Это единственный штатный механизм, который позволяет связать два произвольных запроса и получить на выходе иерархическую структуру (например, ДеревоЗначений).

Рассмотрим, как это работает:

  1. В схеме СКД создаются два набора данных — основной и детальный.
  2. На закладке «Связи наборов данных» указывается поле связи (например, Номенклатура).
  3. В настройках структуры добавляется группировка по Номенклатуре, а внутри нее — группировка по Детальным записям.

Выполнив такую схему программно, вы можете выгрузить результат в дерево, где подчиненные строки каждой ветки и будут представлять собой ту самую «вложенную таблицу».

Рекомендации по выбору метода

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

Таким образом, хотя платформа 1С не позволяет напрямую «конструировать» таблицы значений внутри ячеек произвольного запроса, комбинация правильного упорядочивания и иерархического обхода выборок полностью решает эту задачу.

← На главную