Как передать сложные отборы из настроек СКД в запрос модуля объекта?

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

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

Зачем это нужно и в чем основная сложность?

Проанализируем ситуацию: пользователь зашел в настройки отчета и добавил условие «Контрагент В списке...» и «Дата Больше...». Если наш запрос в модуле объекта жестко прописан без этих условий, он выполнится избыточно. Основная сложность заключается в том, что вид сравнения может быть любым (равно, не равно, в списке, в группе, заполнено и т.д.), и вручную разбирать каждый элемент коллекции КомпоновщикНастроек.Настройки.Отбор.Элементы — задача трудоемкая и чреватая ошибками.

Способ 1: Использование Построителя отчета (классический метод)

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

Выполним следующие шаги для реализации этого метода:

  1. В тексте запроса в модуле объекта подготовим специальные поля в фигурных скобках. Это «подсказки» для построителя, куда именно вставлять динамические условия.
  2. Создадим объект ПостроительОтчета и передадим ему наш текст.
  3. Скопируем отборы из СКД в отборы построителя.
  4. Получим модифицированный текст запроса.

Пример программной реализации в модуле объекта:


Процедура ПриКомпоновкеРезультата(ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // 1. Подготовим базовый текст запроса с конструкциями для построителя
    ТекстЗапроса = 
    "ВЫБРАТЬ
    |	Продажи.Контрагент КАК Контрагент,
    |	Продажи.СуммаОбороcounter КАК Сумма
    |ИЗ
    |	РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи
    |{ГДЕ
    |	Продажи.Контрагент.* КАК Контрагент}";

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

    // 4. Получаем финальный запрос с учетом всех "В списке", "Больше" и т.д.
    ЗапросДляВыполнения = Построитель.ПолучитьЗапрос();
    
    // Устанавливаем стандартные параметры
    ЗапросДляВыполнения.УстановитьПараметр("НачалоПериода", НачалоПериода);
    ЗапросДляВыполнения.УстановитьПараметр("КонецПериода", КонецПериода);
    
    ТаблицаРезультат = ЗапросДляВыполнения.Выполнить().Выгрузить();
    
    // Передаем таблицу в СКД
    ВнешниеНаборыДанных = Новый Структура;
    ВнешниеНаборыДанных.Вставить("ТЗ_Данные", ТаблицаРезультат);
    
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.Настройки, ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(Результат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);

КонецПроцедуры

Способ 2: Использование возможностей БСП (Библиотека стандартных подсистем)

Выясним, как современные типовые конфигурации решают эту задачу. Если в вашей базе есть БСП, то ручное создание построителя не требуется. Проанализируем возможности общих модулей. Существует функция ОбщегоНазначенияКлиентСервер.ПостроитьЗапросПоОтбору() или специализированные методы в КомпоновкаДанныхСервер.

Разберем алгоритм работы с БСП:

  1. Мы передаем функции текст запроса и коллекцию элементов отбора СКД.
  2. Функция сама анализирует ВидСравненияКомпоновкиДанных.
  3. На выходе получаем модифицированный текст и заполненную структуру параметров.

Это самый «чистый» способ для программиста, так как он поддерживает иерархические отборы (группы И / ИЛИ) и корректно обрабатывает специфические виды сравнения, такие как «В иерархии».

Способ 3: Объектная модель запроса (СхемаЗапроса)

Посмотрим на более современный подход, доступный в платформе 8.3 — использование объекта СхемаЗапроса. Этот метод позволяет программно «разобрать» запрос на части (выбираемые поля, источники, условия) и добавить новые условия в секцию ГДЕ без текстовой склейки.

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

Однако при использовании СхемаЗапроса вам все равно придется написать небольшой «парсер», который сопоставит ВидСравненияКомпоновкиДанных.Меньше с оператором < в схеме запроса.

Важный нюанс: Отключение повторной фильтрации

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

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

Резюме

Мы разобрали несколько вариантов решения задачи. Для быстрого результата в старых конфигурациях идеально подходит ПостроительОтчета. Если вы работаете с современной типовой конфигурацией — обязательно используйте функции БСП, это сократит ваш код в несколько раз. Если же требуется максимальный контроль над структурой запроса — ваш выбор СхемаЗапроса. Главное помнить: фильтрация на уровне СУБД (в запросе) всегда в десятки раз быстрее, чем фильтрация готовой таблицы в памяти 1С.

← На главную