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