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

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

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

Ограничения языка запросов СКД

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

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

Способ 1. Использование Менеджера временных таблиц (Рекомендуемый)

Этот способ является наиболее универсальным, если требуется выполнять сложные соединения, подзапросы и группировки между пользовательской таблицей с формы и реальными таблицами базы данных прямо на уровне СУБД. Мы программно создаем временную таблицу до запуска компоновщика и передаем менеджер временных таблиц в процессор компоновки.

Разберем пошаговый алгоритм настройки:

  1. В схеме компоновки данных в наборе данных «Запрос» пишем запрос, который сразу обращается к временной таблице (например, ВТ_Отбор), предполагая, что она уже существует в контексте выполнения:
    
    ВЫБРАТЬ
        ВТ_Отбор.Контрагент КАК Контрагент,
        ВТ_Отбор.СуммаОтбора КАК СуммаОтбора
    ПОМЕСТИТЬ ВТ_ДанныеФормы
    ИЗ
        ВТ_Отбор КАК ВТ_Отбор
    ;
    
    ////////////////////////////////////////////////////////////////////////////////
    ВЫБРАТЬ
        ПродажиОбороты.Контрагент КАК Контрагент,
        ПродажиОбороты.СуммаОборот КАК СуммаОборот,
        ВТ_ДанныеФормы.СуммаОтбора КАК Лимит
    ИЗ
        РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ПродажиОбороты
            ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДанныеФормы КАК ВТ_ДанныеФормы
            ПО ПродажиОбороты.Контрагент = ВТ_ДанныеФормы.Контрагент
    
  2. В модуле объекта отчета переопределяем стандартную обработку в событии ПриКомпоновкеРезультата. Создаем объект МенеджерВременныхТаблиц, формируем временную таблицу с помощью обычного запроса и передаем менеджер в метод инициализации процессора компоновки:
    
    Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
        
        СтандартнаяОбработка = Ложь;
        
        // 1. Инициализируем менеджер временных таблиц
        МенеджерВТ = Новый МенеджерВременныхТаблиц;
        
        // 2. Создаем временную таблицу ВТ_Отбор на основе таблицы значений из отчета
        Запрос = Новый Запрос;
        Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |   ТЗ.Контрагент КАК Контрагент,
            |   ТЗ.СуммаОтбора КАК СуммаОтбора
            |ПОМЕСТИТЬ ВТ_Отбор
            |ИЗ
            |   &ТаблицаОтбора КАК ТЗ";
        
        Запрос.УстановитьПараметр("ТаблицаОтбора", ТаблицаОтбора);
        Запрос.Выполнить();
        
        // 3. Выполняем компоновку с передачей Менеджера ВТ
        Настройки = КомпоновщикНастроек.ПолучитьНастройки();
        
        КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
        МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
        
        ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
        // Передаем МенеджерВТ четвертым параметром метода Инициализировать
        ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина, , МенеджерВТ);
        
        ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
        ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
        ПроцессорВывода.Вывод(ПроцессорКомпоновки);
        
    КонецПроцедуры
    

Способ 2. Использование набора данных типа «Объект»

Если данные таблицы формы не требуют сложных многократных подзапросов в теле SQL-запроса к БД, можно воспользоваться штатным механизмом внешних наборов данных СКД.

Рассмотрим порядок действий для этого варианта:

  1. В схеме компоновки данных добавляем два набора данных: НаборДанных_Запрос (запрос к таблицам БД) и НаборДанных_ТаблицаФормы (тип «Объект», имя содержащего объекта, например, ТаблицаОтбора).
  2. На вкладке «Связи наборов данных» указываем условие связи по ключевым полям (например, по полю Контрагент). СКД выполнит соединение этих двух источников в оперативной памяти в процессе компоновки.
  3. В событии ПриКомпоновкеРезультата передаем таблицу через структуру внешних наборов данных:
    
    Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
        
        СтандартнаяОбработка = Ложь;
        
        ВнешниеНаборыДанных = Новый Структура;
        ВнешниеНаборыДанных.Вставить("ТаблицаОтбора", ТаблицаОтбора);
        
        Настройки = КомпоновщикНастроек.ПолучитьНастройки();
        
        КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
        МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
        
        ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
        ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
        
        ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
        ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
        ПроцессорВывода.Вывод(ПроцессорКомпоновки);
        
    КонецПроцедуры
    

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

В ситуациях, когда таблица отбора содержит буквально несколько строк и создание менеджера временных таблиц избыточно, можно программно сформировать блок ВЫБРАТЬ ... ОБЪЕДИНИТЬ ВСЕ перед выполнением отчета.

Посмотрим на пример формирования такого блока:


ТекстВыборки = "";
Индекс = 0;
Для Каждого Стр Из ТаблицаОтбора Цикл
    Если Индекс > 0 Тогда
        ТекстВыборки = ТекстВыборки + " ОБЪЕДИНИТЬ ВСЕ ";
    КонецЕсли;
    ТекстВыборки = ТекстВыборки + "ВЫБРАТЬ &Контрагент" + Индекс + " КАК Контрагент, &Лимит" + Индекс + " КАК Лимит";
    Индекс = Индекс + 1;
КонецЦикла;

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

Важные технические нюансы: типизация и индексация

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

  1. Строгая типизация колонок: При программном создании таблицы значений на форме всегда задавайте явное описание типов колонок (используя КвалификаторыЧисла, КвалификаторыСтроки, ссылки на справочники). Если тип колонки останется нетипизированным (Произвольный), запрос 1С может завершиться ошибкой несовместимости типов при выполнении операций соединения.
  2. Индексация полей соединения: Если в таблице отбора может быть более нескольких десятков строк, обязательно добавляйте инструкцию ИНДЕКСИРОВАТЬ ПО в запросе формирования временной таблицы. Это предотвратит сканирование всей таблицы при выполнении соединений и значительно ускорит формирование отчета.
← На главную