При работе с Системой компоновки данных (СКД) разработчики часто сталкиваются с необходимостью реализовать гибкую фильтрацию. Типичная задача: если параметр заполнен — фильтруем по нему, если нет — применяем особую логику (например, исключаем определенные записи). Однако попытка написать сложную конструкцию ВЫБОР КОГДА внутри расширения языка запросов (фигурных скобок {}) часто приводит к ошибке "Параметр не указан". Разберем, почему это происходит и как правильно решить эту задачу несколькими способами.
Проанализируем ситуацию. Когда мы помещаем условие в фигурные скобки {ГДЕ...}, мы даем команду препроцессору СКД: "Если параметры внутри этого блока не заполнены, удали весь этот блок из итогового запроса". Однако препроцессор СКД эффективно работает только с простыми сравнениями. Если внутри скобок оказывается сложная логика ВЫБОР КОГДА, препроцессор не всегда может корректно определить, "пустое" ли это условие. В результате часть выражения с параметром остается в запросе, и если пользователь не заполнил значение, СУБД выдает ошибку.
Кроме того, использование ВЫБОР в секции ГДЕ крайне негативно сказывается на производительности. СУБД не может использовать индексы для таких условий (проблема SARGability), что приводит к полному сканированию таблиц (Full Table Scan) и замедлению отчета в десятки раз.
Вместо того чтобы пытаться уместить всю логику в одну конструкцию ВЫБОР, разберем, как сделать условия независимыми. Рассмотрим пример, где нам нужно фильтровать по организации и подразделению. Мы можем прописать каждое условие отдельно в фигурных скобках:
{ГДЕ
ХозрасчетныйОстатки.Организация = &Организация,
ХозрасчетныйОстатки.Подразделение = &Подразделение}
В этом случае, если &Подразделение не заполнено, СКД просто выкинет вторую строку, оставив только фильтр по организации. Если же нам нужна специфическая логика (например, "если подразделение не указано, исключить обособленные"), лучше вынести это в программную обработку или в параметры самой виртуальной таблицы.
Посмотрим на более изящный способ настройки через вкладку "Параметры" в схеме компоновки данных. Мы можем создать скрытый параметр, значение которого будет вычисляться автоматически. Это позволяет оставить текст запроса чистым и производительным.
Шаги реализации:
&СписокПодразделений.
ВЫБОР
КОГДА ЗначениеЗаполнено(&Подразделение)
ТОГДА &Подразделение
ИНАЧЕ ПолучитьСписокПодразделенийБезОбособленных(&Организация)
КОНЕЦ
В самом запросе теперь достаточно написать простое и эффективное условие:
{ГДЕ
ХозрасчетныйОстатки.Подразделение В (&СписокПодразделений)}
Таким образом, СУБД получит готовый список значений или конкретную ссылку, что позволит ей эффективно использовать индексы.
Проанализируем работу с виртуальными таблицами, такими как ХозрасчетныйОстатки. Важнейшее правило оптимизации в 1С: условия должны передаваться в параметры виртуальной таблицы, а не в секцию ГДЕ. Если фильтр стоит в ГДЕ, система сначала выберет ВСЕ остатки в память, а потом отсечет лишнее. Если фильтр в параметрах таблицы — выборка ограничится на уровне базы данных.
Правильный способ прописать расширение для подразделения в параметрах виртуальной таблицы выглядит так:
ХозрасчетныйОстатки.Остатки(&Период, , , {Подразделение.* КАК Подразделение, Организация.* КАК Организация})
Обратите внимание на конструкцию {Подразделение.* КАК Подразделение}. Она позволяет СКД автоматически подставлять выбранные пользователем отборы (включая виды сравнения "В списке", "Равно" и т.д.) непосредственно в параметры получения остатков.
Если логика фильтрации слишком сложна для языка запросов, мы можем воспользоваться программным методом. Выясним причину, по которой автор темы выбрал этот вариант: это дает полный контроль над текстом запроса перед его выполнением.
Рассмотрим пример кода в модуле объекта отчета:
Процедура ПриКомпоновкеРезультата(ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получаем настройки пользователя
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
// Проверяем, заполнено ли подразделение в параметрах или отборах
ПараметрПодразделение = Настройки.ПараметрыДанных.Элементы.Найти("Подразделение");
Если ПараметрПодразделение <> Неопределено И НЕ ЗначениеЗаполнено(ПараметрПодразделение.Значение) Тогда
// Если не заполнено, меняем текст запроса или добавляем свой отбор
ТекстЗапроса = СхемаКомпоновкиДанных.НаборыДанных.НаборДанных1.Запрос;
// Логика замены текста (упрощенно)
НовоеУсловие = "И НЕ ХозрасчетныйОстатки.Подразделение.ОбособленноеПодразделение";
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "//*УсловиеПодразделение*", НовоеУсловие);
СхемаКомпоновкиДанных.НаборыДанных.НаборДанных1.Запрос = ТекстЗапроса;
КонецЕсли;
// Далее стандартный вывод...
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Для создания качественного и быстрого отчета в СКД при работе с необязательными параметрами следует придерживаться следующих рекомендаций:
Остатки() или Обороты().Помните, что пустая ссылка и неуказанный параметр — это разные вещи для СКД. Если в параметре передана пустая ссылка, СКД считает его заполненным. Чтобы условие полностью исчезло из SQL-запроса, параметр должен быть либо не установлен в настройках, либо выключен (флажок использования).