При разработке сложных отчетов или обработок на платформе 1С часто возникает задача динамического изменения структуры запроса. Одной из таких задач является включение или исключение целых блоков данных, соединяемых через оператор ОБЪЕДИНИТЬ ВСЕ, в зависимости от переданного пользователем параметра. Рассмотрим подробнее, какими способами можно добиться этого результата, проанализируем их преимущества и недостатки.
Это самый востребованный метод, когда необходимо оставить текст запроса неизменным (статическим). Мы добавим во вторую (или последующую) часть объединения условие, которое будет проверять значение нашего параметра. Если параметр ложен, вторая часть запроса вернет пустой результат, и объединять будет фактически нечего.
Разберем этот метод на примере кода:
ВЫБРАТЬ
Продажи.Номенклатура,
Продажи.Количество
ИЗ
РегистрНакопления.Продажи КАК Продажи
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Возвраты.Номенклатура,
Возвраты.Количество
ИЗ
РегистрНакопления.Возвраты КАК Возвраты
ГДЕ
&ДелатьОбъединение = ИСТИНА
В данном случае, если в параметр &ДелатьОбъединение передать значение Ложь, СУБД отсечет все записи из второй таблицы еще на этапе выполнения. Этот подход позволяет сохранить структуру запроса в одном объекте Запрос, что удобно для отладки в консоли запросов.
Если мы хотим добиться максимальной производительности и не нагружать СУБД лишними частями плана выполнения, лучше всего использовать программное формирование текста. Мы можем подготовить базовый текст и добавлять к нему блок с ОБЪЕДИНИТЬ ВСЕ только при выполнении условия в коде 1С.
Проанализируем ситуацию, когда текст запроса хранится в переменной:
ТекстЗапроса =
"ВЫБРАТЬ
| Товары.Ссылка КАК Товар
|ИЗ
| Справочник.Номенклатура КАК Товары";
Если Параметры.ДобавитьУслуги Тогда
ТекстЗапроса = ТекстЗапроса +
"
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ
| Услуги.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Услуги
|ГДЕ
| Услуги.ЭтоУслуга = ИСТИНА";
КонецЕсли;
Запрос = Новый Запрос(ТекстЗапроса);
Также можно использовать функцию СтрЗаменить, если в шаблоне запроса заранее подготовлена строка-маркер или если вы хотите динамически удалять уже написанный блок. Однако будьте осторожны: прямая замена текста ОБЪЕДИНИТЬ ВСЕ на пустую строку может привести к синтаксической ошибке, если после оператора идет текст второго запроса. Нужно удалять весь блок целиком.
Для динамического управления структурой запроса в платформе предусмотрен объект СхемаЗапроса. Это наиболее надежный метод, так как он работает не со строками, а с объектной моделью. Выясним, как это работает: СхемаЗапроса позволяет программно обращаться к коллекции ПакетЗапросов и Операторы.
Посмотрим на примерный алгоритм работы:
СхемаЗапроса и загружаем в него текущий текст.Операторы лишний элемент (каждый оператор в коллекции после первого — это и есть часть объединения).Запрос.Этот метод гарантирует, что вы не допустите опечаток в ключевых словах, которые возможны при ручной склейке строк.
Для повышения производительности, особенно в сложных отчетах, рекомендуется использовать временные таблицы. Рассмотрим сценарий, при котором мы помещаем данные для объединения во временную таблицу только по условию. Если условие не выполнено, мы можем создать пустую временную таблицу с той же структурой.
Такой подход позволяет оптимизатору MS SQL Server или PostgreSQL более эффективно строить план запроса. Выглядит это примерно так:
// В коде 1С определяем, какой запрос выполнить
Если &НужноОбъединять Тогда
ТекстЧасти2 = "ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Данные2 ИЗ ...";
Иначе
// Создаем пустую таблицу с той же схемой, чтобы итоговый запрос не упал
ТекстЧасти2 = "ВЫБРАТЬ Первые 0 ... ПОМЕСТИТЬ ВТ_Данные2 ИЗ ...";
КонецЕсли;
В итоговом запросе пакета мы просто пишем ОБЪЕДИНИТЬ ВСЕ из этих двух временных таблиц. Поскольку одна из них может быть пустой (благодаря ВЫБРАТЬ ПЕРВЫЕ 0), итоговый результат будет корректным, а нагрузка на сервер минимальной.
При использовании любого из перечисленных методов следует учитывать несколько критически важных моментов:
ОБЪЕДИНИТЬ ВСЕ крайне важно, чтобы типы колонок во всех объединяемых частях совпадали. Если в первой части колонка имеет тип Число, а во второй (которая отсекается условием ГДЕ) — тип Строка, запрос может отработать некорректно или вызвать ошибку приведения типов.ГДЕ &Параметр: Помните, что конструкция ГДЕ &Параметр = ИСТИНА иногда заставляет СУБД подготавливать данные из таблиц, прежде чем применить фильтр по параметру. Для очень тяжелых таблиц предпочтительнее использовать программную сборку текста запроса или пакетный режим.Таким образом, выбор метода зависит от сложности задачи. Для простых случаев достаточно логического условия в ГДЕ, а для сложных архитектурных решений лучше использовать СхемаЗапроса или динамическое формирование пакета временных таблиц.