Как в запросе 1С сгруппировать данные по связанным документам?

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

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

Ограничение использования критериев отбора в запросах

Первое решение, которое часто приходит на ум разработчику — использовать виртуальную таблицу критерия отбора КритерийОтбора.СвязанныеДокументы. В запросе для одного документа это выглядит следующим образом:


ВЫБРАТЬ
    СвязанныеДокументы.Ссылка КАК Ссылка
ИЗ
    КритерийОтбора.СвязанныеДокументы(&ДокументОснование) КАК СвязанныеДокументы

Однако при попытке выбрать связанные документы сразу для целого списка документов или соединить виртуальную таблицу критерия отбора с другой таблицей в запросе мы сталкиваемся с платформенным ограничением. Виртуальная таблица КритерийОтбора.<ИмяКритерия> может принимать в качестве параметра исключительно фиксированное значение — параметр запроса или литерал. В языке запросов 1С невозможно передать в параметр критерия отбора поле из другой таблицы пакета или соединить его по динамическому условию. Вызов же запроса к критерию отбора в цикле обхода выборки приводит к существенному падению производительности при росте объема данных. Разберем оптимальные и быстрые способы решения этой задачи.

Способ 1. Прямое соединение таблиц документов (оптимально для произвольного запроса)

Проанализируем структуру хранения данных. Механизм критерия отбора в метаданных не создает скрытых служебных таблиц связей, а лишь описывает реквизиты объектов конфигурации, в которых может храниться ссылка на документ. Это означает, что подчиненный документ всегда ссылается на исходный через явный реквизит шапки (например, ДокументОснование) либо через реквизит табличной части (например, ДокументПередачи).

Рассмотрим построение запроса с использованием временных таблиц и соединения. Если документ возврата ссылается на передачу в своей табличной части, сначала выберем уникальные пары документов во временную таблицу:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВозвратТовары.Ссылка КАК ДокументВозврата,
    ВозвратТовары.ДокументПередачи КАК ДокументПередачи
ПОМЕСТИТЬ ВТ_Связи
ИЗ
    Документ.ВозвратТоваровИзПереработки.Товары КАК ВозвратТовары
ГДЕ
    ВозвратТовары.ДокументПередачи ССЫЛКА Документ.ПередачаТоваровНаПереработку
;

ВЫБРАТЬ
    ПередачаТовары.Номенклатура КАК Номенклатура,
    ПередачаТовары.Ссылка КАК ДокументПередачи,
    ВТ_Связи.ДокументВозврата КАК ДокументВозврата
ИЗ
    Документ.ПередачаТоваровНаПереработку.Товары КАК ПередачаТовары
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Связи КАК ВТ_Связи
        ПО ПередачаТовары.Ссылка = ВТ_Связи.ДокументПередачи
ИТОГИ ПО
    Номенклатура,
    ДокументПередачи

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

Способ 2. Использование связей наборов данных в СКД

Если задача решается в рамках Системы компоновки данных, мы можем использовать механизм критерия отбора без написания циклов в коде. Платформа берет оптимизацию пакетных запросов на себя. Разберем по шагам, как это настроить:

  1. Создадим Набор данных 1 (Запрос), который выбирает номенклатуру и исходные документы передачи товаров:
    
    ВЫБРАТЬ
        Передача.Ссылка КАК ДокументПередачи,
        Передача.Номенклатура КАК Номенклатура
    ИЗ
        Документ.ПередачаТоваровНаПереработку.Товары КАК Передача
    
  2. Создадим Набор данных 2 (Запрос), обращающийся к критерию отбора:
    
    ВЫБРАТЬ
        СвязанныеДокументы.Ссылка КАК ДокументВозврата
    ИЗ
        КритерийОтбора.СвязанныеДокументы(&ДокОснование) КАК СвязанныеДокументы
    
  3. Перейдем на закладку «Связи наборов данных» и добавим новую строку:
    1. Источник связи: Набор данных 1.
    2. Приемник связи: Набор данных 2.
    3. Условие связи: поле ДокументПередачи связывается с параметром ДокОснование.
  4. На закладке «Настройки» зададим нужную иерархию группировок: Номенклатура -> ДокументПередачи -> ДокументВозврата.

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

Способ 3. Пакетная выборка через массив ссылок в коде

Если результат необходимо обработать программно без компоновки данных, правильной альтернативой запросу в цикле является пакетная выборка с условием вхождения. Разберем пример реализации:


// Шаг 1. Получаем список документов-оснований
ЗапросПередачи = Новый Запрос;
ЗапросПередачи.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |   Передача.Ссылка КАК Ссылка
    |ИЗ
    |   Документ.ПередачаТоваровНаПереработку КАК Передача";

МассивПередач = ЗапросПередачи.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

// Шаг 2. Выбираем все подчиненные документы одним запросом
ЗапросВозвраты = Новый Запрос;
ЗапросВозвраты.Текст = 
    "ВЫБРАТЬ
    |   ВозвратТовары.Ссылка КАК ДокументВозврата,
    |   ВозвратТовары.ДокументПередачи КАК ДокументПередачи
    |ИЗ
    |   Документ.ВозвратТоваровИзПереработки.Товары КАК ВозвратТовары
    |ГДЕ
    |   ВозвратТовары.ДокументПередачи В (&МассивПередач)";

ЗапросВозвраты.УстановитьПараметр("МассивПередач", МассивПередач);
ТаблицаВозвратов = ЗапросВозвраты.Выполнить().Выгрузить();

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

Такой подход исключает отправку множества мелких запросов к СУБД и обеспечивает максимальное быстродействие при программном обходе.

Способ 4. Получение данных через регистры учета

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

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

← На главную