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