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