Как динамически формировать условия в тексте запроса 1С без потери совместимости с конструктором?

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

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

Способ 1. Использование маркеров-заглушек и функции СтрЗаменить

Рассмотрим популярный прием, при котором в статичном тексте запроса заранее объявляются условия-заглушки. Это позволяет без проблем открывать и редактировать текст запроса в Конструкторе, а перед выполнением запроса заменять заглушки на реальные условия на встроенном языке 1С.

Посмотрим на пример с использованием истинных предикатов:


Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |	Заказы.Номенклатура КАК Номенклатура,
    |	Заказы.КоличествоОстаток КАК КоличествоОстаток
    |ИЗ
    |	РегистрНакопления.ЗаказыКлиентов.Остатки КАК Заказы
    |ГДЕ
    |	1 = 1
    |	И Заказы.КоличествоОстаток > 0";

Если ЗначениеЗаполнено(Контрагент) Тогда
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "1 = 1", "Заказы.ЗаказКлиента.Контрагент В ИЕРАРХИИ(&Контрагент)");
    Запрос.УстановитьПараметр("Контрагент", Контрагент);
КонецЕсли;

Проанализируем аналогичный прием для динамического выбора полей, который встречается в типовых конфигурациях (например, при переключении между кодом, артикулом или наименованием товара):


Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |	ВложенныйЗапрос.Номенклатура КАК Номенклатура,
    |	ВЫРАЗИТЬ(ВложенныйЗапрос.Номенклатура.НаименованиеПолное КАК СТРОКА(1000)) КАК ТоварНаименование,
    |	0 КАК ТоварКод
    |ИЗ
    |	Справочник.Номенклатура КАК ВложенныйЗапрос";

Если ИспользоватьАртикул Тогда
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "0 КАК ТоварКод", "ВложенныйЗапрос.Номенклатура.Артикул КАК ТоварКод");
Иначе
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "0 КАК ТоварКод", "ВложенныйЗапрос.Номенклатура.Код КАК ТоварКод");
КонецЕсли;

Важное предостережение: Использование простых чисел и конструкций вроде 1 = 1 или 0 в качестве маркеров замены несет риски ложных срабатываний, если эти цифры встретятся в строковых литералах, числовых константах или других частях запроса. Для повышения надежности рекомендуется использовать уникальные семантические маркеры, например &_УсловиеКонтрагент или комментарии-метки, обернутые в валидный синтаксис.

Способ 2. Синтаксис Построителя запроса (фигурные скобки)

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

Разберем пример с использованием объекта ПостроительЗапроса:


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

// Добавляем отбор только при необходимости
Если ЗначениеЗаполнено(Контрагент) Тогда
    ЭлементОтбора = Построитель.Отбор.Добавить("Контрагент");
    ЭлементОтбора.ВидСравнения = ВидСравнения.ВИерархии;
    ЭлементОтбора.Значение = Контрагент;
    ЭлементОтбора.Использование = Истина;
КонецЕсли;

Результат = Построитель.Результат;

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

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

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

Посмотрим на реализацию:


МассивУсловий = Новый Массив;

// Обязательные условия
МассивУсловий.Добавить("Заказы.КоличествоОстаток > 0");

// Динамические условия
Если ЗначениеЗаполнено(Контрагент) Тогда
    МассивУсловий.Добавить("Заказы.ЗаказКлиента.Контрагент В ИЕРАРХИИ(&Контрагент)");
КонецЕсли;

Если ЗначениеЗаполнено(Склад) Тогда
    МассивУсловий.Добавить("Заказы.Склад = &Склад");
КонецЕсли;

ТекстУсловий = СтрСоединить(МассивУсловий, Символы.ПС + "	И ");

ШаблонЗапроса =
    "ВЫБРАТЬ
    |	Заказы.Номенклатура КАК Номенклатура,
    |	Заказы.КоличествоОстаток КАК КоличествоОстаток
    |ИЗ
    |	РегистрНакопления.ЗаказыКлиентов.Остатки КАК Заказы
    |ГДЕ
    |	%1";

Запрос = Новый Запрос;
Запрос.Текст = СтрШаблон(ШаблонЗапроса, ТекстУсловий);

Если ЗначениеЗаполнено(Контрагент) Тогда
    Запрос.УстановитьПараметр("Контрагент", Контрагент);
КонецЕсли;
Если ЗначениеЗаполнено(Склад) Тогда
    Запрос.УстановитьПараметр("Склад", Склад);
КонецЕсли;

Для открытия такого запроса в Конструкторе достаточно временно подставить в секцию ГДЕ любое базовое условие (например, ИСТИНА), настроить поля и таблицы, после чего вернуть вызов функции СтрШаблон.

Способ 4. Программное использование Схемы компоновки данных (СКД)

Для сложных аналитических отчетов и выборок с большим числом необязательных фильтров наиболее производительным решением является программная работа с СКД через объекты СхемаКомпоновкиДанных и КомпоновщикМакетаКомпоновкиДанных.

СКД обладает ключевыми преимуществами:

  1. Автоматически удаляет неиспользуемые поля, соединения и отборы из итогового SQL-запроса.
  2. Исключает проблему ложных замен строк в коде.
  3. Обеспечивает максимальную скорость работы СУБД за счет генерации строго необходимого текста запроса под выбранные пользователем настройки.

Влияние динамических условий на производительность и планы выполнения СУБД

Выясним причину, по которой к динамическим условиям нужно подходить с осторожностью с точки зрения оптимизации СУБД:

  1. Неиспользуемые предикаты вида 1 = 1: Хотя современные СУБД (PostgreSQL, MS SQL Server) легко оптимизируют тривиальные выражения, большое количество лишних условий в сложных запросах может искажать статистику селективности и оценку кардинальности, приводя к неоптимальному выбору индексов (например, замене Index Seek на полное сканирование Table Scan).
  2. Условия с ИЛИ в параметрах: Попытка избежать строковых замен конструкцией ГДЕ (&НеОтбирать ИЛИ Таблица.Поле = &Поле) часто блокирует использование индексов по полю отбора. СУБД вынуждена сканировать всю таблицу. Динамическая генерация текста запроса (когда условие либо есть, либо отсутствует вовсе) работает в разы быстрее.
  3. Кэширование планов запросов: Постоянное изменение структуры текста запроса создает новые планы выполнения в кэше СУБД. При высокой частоте вызовов рекомендуется использовать стандартизированные шаблоны условий.
← На главную