В практике разработки на платформе 1С:Предприятие часто возникает задача реализации «необязательного» фильтра. Это ситуация, когда пользователь на форме может либо выбрать конкретное значение для отбора (например, конкретную станцию), либо оставить поле пустым, чтобы увидеть все записи. Сегодня мы разберем, почему популярный, но «ленивый» способ с использованием оператора ПОДОБНО является плохой практикой, и рассмотрим наиболее эффективные методы решения этой задачи.
Проанализируем исходную ситуацию. Разработчик пытается передать в запрос параметр, который в зависимости от условия принимает либо конкретное название станции, либо символ %. В тексте запроса это выглядит так:
ГДЕ
Дислокация.СтанцияНазначения ПОДОБНО &СтанцияНазначения
Если в коде 1С мы напишем:
Если СтанцияВыбрана Тогда
СтанцияНазначения = "ХАБАРОВСК I";
Иначе
СтанцияНазначения = "%";
КонецЕсли;
На первый взгляд, это кажется изящным решением: если передан %, СУБД вернет все строки. Однако здесь кроется серьезная проблема производительности. Давайте выясним причину. Когда мы используем ПОДОБНО с маской в начале или просто состоящей из одного спецсимвола, запрос перестает быть SARGable (Search Argumentable). Это технический термин, означающий, что оптимизатор SQL не может использовать индексы для поиска данных.
Вместо быстрого поиска по индексу (Index Seek), сервер вынужден выполнять полное сканирование таблицы или индекса (Index Scan/Table Scan). Если ваш регистр Дислокация содержит сотни тысяч или миллионы записей, такой запрос превратится в «убийцу» производительности, заставляя пользователей ждать и нагружая серверную инфраструктуру.
Рассмотрим более правильный подход, который позволяет избежать использования оператора ПОДОБНО. Мы можем передать в запрос дополнительный булев параметр, который будет указывать, нужно ли вообще применять фильтр. Разберем этот пример по шагам.
Изменим текст запроса следующим образом:
ГДЕ
(НЕ &ИспользоватьОтборПоСтанции ИЛИ Дислокация.СтанцияНазначения = &СтанцияНазначения)
Теперь проанализируем логику работы этого условия:
ИспользоватьОтборПоСтанции в значение Ложь. Условие превращается в (Истина ИЛИ ...). По правилам булевой логики, все выражение становится истинным для каждой строки, и фильтрация не происходит.ИспользоватьОтборПоСтанции в Истина. Условие превращается в (Ложь ИЛИ СтанцияНазначения = &СтанцияНазначения). В этом случае эффективно работает обычное равенство, которое СУБД легко обрабатывает с использованием индексов.Этот метод значительно лучше, чем ПОДОБНО, однако у него есть небольшой нюанс, называемый Parameter Sniffing. SQL-сервер может закешировать план выполнения, который был оптимален для «пустого» фильтра, и использовать его для поиска конкретной станции, что будет не совсем эффективно. Но для большинства задач в 1С это решение является приемлемым и стабильным.
Если мы стремимся к максимальной производительности, разберем самый «чистый» с точки зрения СУБД способ — динамическое построение условия ГДЕ. Посмотрим на пример кода:
ТекстУсловия = "";
Если ЗначениеЗаполнено(СтанцияНазначенияНаФорме) Тогда
ТекстУсловия = "И Дислокация.СтанцияНазначения = &СтанцияНазначения";
Запрос.УстановитьПараметр("СтанцияНазначения", СтанцияНазначенияНаФорме);
КонецЕсли;
Запрос.Текст =
"ВЫБРАТЬ
| Дислокация.НомерПоезда
|ИЗ
| РегистрСведений.Дислокация КАК Дислокация
|ГДЕ
| Дислокация.ДатаОперации В(&ДатаОперации) " + ТекстУсловия;
Почему этот метод считается лучшим? Проанализируем ситуацию: если отбор не нужен, в итоговом тексте запроса, который уйдет на SQL-сервер, вообще не будет лишних проверок. СУБД получит максимально простой и понятный текст. Если же отбор нужен, будет использовано прямое равенство =, что гарантирует использование индекса по полю СтанцияНазначения.
Проанализируем еще несколько важных моментов, которые часто упускают разработчики при работе с оператором ПОДОБНО:
ПОДОБНО работает только со строками. Если ваше поле СтанцияНазначения является ссылкой на справочник (СправочникСсылка.ЖелезнодорожныеСтанции), то использование ПОДОБНО вызовет неявное приведение типа к строке. Это катастрофически замедляет запрос, так как СУБД не может использовать индекс по ссылочному полю.ПОДОБНО "ХАБАРОВСК%", вы рискуете выбрать лишние данные. Например, если в базе есть «ХАБАРОВСК I» и «ХАБАРОВСК-ПОРТ», оператор выберет оба значения. Если бизнес-логика требует выбора конкретной станции, всегда используйте строгое равенство = или оператор В (МассивСтанций).ПОДОБНО учитываются все символы, включая концевые пробелы в строковых полях фиксированной длины. Это часто становится причиной того, что запрос «ничего не находит», хотя визуально данные совпадают. Обычное равенство = в большинстве случаев игнорирует концевые пробелы, что делает его более надежным.Подводя итог нашему разбору, можно составить четкий алгоритм действий для реализации эффективных фильтров:
Во-первых, навсегда откажитесь от использования ПОДОБНО "%" как способа «отключить» фильтр. Это создает избыточную нагрузку на сервер и замедляет работу всей системы.
Во-вторых, если вы пишете простой отчет или обработку, используйте конструкцию (НЕ &Флаг ИЛИ Поле = &Значение). Это сделает ваш код читаемым и избавит от проблем с производительностью ПОДОБНО.
В-третьих, для высоконагруженных систем или сложных аналитических запросов выбирайте динамическое формирование текста запроса. Это позволит СУБД строить наиболее оптимальные планы выполнения, используя все доступные индексы.
Помните, что маленькие ошибки в запросах к большим таблицам (таким как регистры дислокации или обороты) имеют свойство накапливаться, превращаясь в общие проблемы производительности информационной базы.