Как динамически формировать условия и поля выборки в запросах 1С?

Программист 1С v8.3 (Управляемые формы)
← На главную

При разработке отчетов и обработок в 1С часто возникает необходимость в динамическом изменении запросов. Например, мы хотим, чтобы определенные условия отбора или даже поля в выборке появлялись только тогда, когда пользователь устанавливает соответствующую настройку. Рассмотрим подробнее, как мы можем реализовать такую логику в запросах 1С, основываясь на практическом опыте и различных подходах.

Исходная проблема: динамический отбор и выборка по городам

Представим ситуацию: у нас есть форма с флажком Поиск по Городам. Если этот флажок установлен, мы должны искать данные по выбранному городу; если нет – отбор по городу применяться не должен. Кроме того, требовалось, чтобы само поле Город включалось в список выбираемых полей только при активации поиска по городам.

Начальный код для формирования условий отбора выглядел следующим образом:


Текст = "";
    Если ПоискПоГородам Тогда
        Если ЗначениеЗаполнено(Города) Тогда
            Текст = Текст + ?(Текст = "", "", " И ") + "
            |        Субконто3.Город = &Город
            |        ";
            Запрос.УстановитьПараметр("Город", Города);
        КонецЕсли;
КонецЕсли;
Возврат Текст;

Этот фрагмент кода корректно формирует часть условия для секции ГДЕ, используя строковую конкатенацию. Однако основная сложность возникла при попытке динамически включать или исключать поле Город из секции ВЫБРАТЬ. Прямая подстановка типа Выбор Когда (ПоискПоГородам = Истина) Тогда (ГотоваяТабл.Город КАК Город,), как и ожидалось, не работала, поскольку синтаксис 1С для такого случая иной.

Обзор подходов к динамическому формированию запросов

В 1С существует несколько эффективных способов динамического построения запросов. Мы разберем их по шагам, от простых до более комплексных, каждый из которых имеет свои преимущества и недостатки.

  1. Использование условных операторов `ВЫБОР КОГДА` непосредственно в запросе

    Этот подход является одним из наиболее "чистых" и рекомендуемых, так как он позволяет встроить динамическую логику прямо в текст запроса, не прибегая к сложной строковой конкатенации. Это сохраняет возможность использования конструктора запросов 1С, что значительно упрощает отладку и поддержку.

    Пример для секции `ГДЕ` (динамический отбор):

    Мы можем использовать конструкцию `ВЫБОР КОГДА <Условие> ТОГДА <Выражение1> ИНАЧЕ <Выражение2> КОНЕЦ` в секции `ГДЕ`. В нашем случае, если флаг &ОтборПоГородам истинен, применяется условие по городу; если ложен – условие всегда будет истинным (`ИСТИНА`), и отбор по городу не будет влиять на результат запроса.

    
    ВЫБРАТЬ
        БизнесРегионы.Наименование КАК Наименование
    ИЗ
        Справочник.БизнесРегионы КАК БизнесРегионы
    ГДЕ
        ВЫБОР
            КОГДА &ОтборПоГородам
                ТОГДА БизнесРегионы.Город В (&Города)
                ИНАЧЕ ИСТИНА
            КОНЕЦ
    

    Перед выполнением такого запроса нам потребуется установить булев параметр &ОтборПоГородам и, если требуется отбор, параметр &Города:

    
    Запрос = Новый Запрос;
    Запрос.Текст = "
        |ВЫБРАТЬ
        |    БизнесРегионы.Наименование КАК Наименование
        |ИЗ
        |    Справочник.БизнесРегионы КАК БизнесРегионы
        |ГДЕ
        |    ВЫБОР
        |        КОГДА &ОтборПоГородам
        |            ТОГДА БизнесРегионы.Город В (&Города)
        |            ИНАЧЕ ИСТИНА
        |        КОНЕЦ
        |";
        
    Запрос.УстановитьПараметр("ОтборПоГородам", ПоискПоГородам); // ПоискПоГородам - булева переменная из формы
    
    Если ПоискПоГородам И ЗначениеЗаполнено(Города) Тогда
        МассивГородов = Новый Массив; // Если Города может быть множественным выбором
        МассивГородов.Добавить(Города); // Или передаем уже готовый массив/список значений
        Запрос.УстановитьПараметр("Города", МассивГородов);
    КонецЕсли;
    
    РезультатЗапроса = Запрос.Выполнить();
    

    Пример для секции `ВЫБРАТЬ` (динамическое включение поля):

    Если мы хотим, чтобы поле Город присутствовало в результате запроса, но его значение было условно, мы можем использовать `ВЫБОР КОГДА` прямо в списке выбираемых полей. Если же требуется полное отсутствие колонки в результате, то такой подход не сработает, и придется прибегать к строковой конкатенации всей секции `ВЫБРАТЬ`.

    
    ВЫБРАТЬ 
        ГотоваяТабл.ИмяКлиента,
        ВЫБОР КОГДА &ПоискПоГородам ТОГДА ГотоваяТабл.Город ИНАЧЕ NULL КОНЕЦ КАК Город
    ИЗ
        ГотоваяТабл
    // ... остальная часть запроса
    

    В этом примере колонка Город всегда будет присутствовать в результате запроса. Если `&ПоискПоГородам` истина, она будет содержать значение из поля ГотоваяТабл.Город; если ложь – значение NULL.

    Преимущества:

    • Читаемость: Логика динамического отбора видна непосредственно в запросе.
    • Конструктор запросов: Сохраняется возможность использования встроенного конструктора запросов 1С.
    • Безопасность: Меньше риск ошибок при форматировании текста запроса.
  2. Динамическая конкатенация строк запроса

    Этот метод был изначально использован автором и является наиболее гибким, когда речь идет о кардинальном изменении структуры запроса – например, добавлении целых секций, изменении связей или полного исключения колонок из выборки. Однако его главный недостаток – полная потеря возможности использования конструктора запросов, что может усложнить поддержку больших и сложных запросов.

    Пример для `ГДЕ` (добавление условия):

    Мы можем сформировать текст условия и добавить его к основному запросу:

    
    ТекстЗапроса = "
        |ВЫБРАТЬ
        |    Документ.Наименование
        |ИЗ
        |    Документ.РеализацияТоваров КАК Документ
        |";
    
    УсловиеПоГородам = "";
    Если ПоискПоГородам Тогда
        Если ЗначениеЗаполнено(Города) Тогда
            УсловиеПоГородам = "ГДЕ Документ.Город = &Город";
            Запрос.УстановитьПараметр("Город", Города);
        КонецЕсли;
    КонецЕсли;
    
    Запрос.Текст = ТекстЗапроса + УсловиеПоГородам;
    

    Или, как было предложено в сообщении 8, использовать сокращенную форму:

    
    ТекстЗапроса = ТекстЗапроса + ?(ОтборПоГородам," ГДЕ БизнесРегионы.Город В (&Города)","");
    

    Важный момент (из сообщения 9): если в запросе уже есть секция `ГДЕ` со статичным условием, то новые динамические условия удобнее добавлять через оператор `И`:

    
    ТекстЗапроса = "
        |ВЫБРАТЬ
        |    Документ.Наименование
        |ИЗ
        |    Документ.РеализацияТоваров КАК Документ
        |ГДЕ
        |    Документ.ПометкаУдаления = ЛОЖЬ
        |";
    
    Если ПоискПоГородам И ЗначениеЗаполнено(Города) Тогда
        ТекстЗапроса = ТекстЗапроса + " И Документ.Город = &Город";
        Запрос.УстановитьПараметр("Город", Города);
    КонецЕсли;
    

    Пример для `ВЫБРАТЬ` (динамическое включение/исключение поля):

    Если нам нужно, чтобы колонка Город появлялась в выборке только при активном флаге, строковая конкатенация – это единственный способ полностью управлять составом колонок:

    
    ПеременныеПоляВыборки = "
        |    ГотоваяТабл.ИмяКлиента КАК Клиент
        |";
    
    Если ПоискПоГородам Тогда
        ПеременныеПоляВыборки = ПеременныеПоляВыборки + ",
        |    ГотоваяТабл.Город КАК Город
        |";
    КонецЕсли;
    
    ТекстЗапроса = "
        |ВЫБРАТЬ " + ПеременныеПоляВыборки + "
        |ИЗ
        |    ГотоваяТабл КАК ГотоваяТабл
        |";
    Запрос.Текст = ТекстЗапроса;
    

    Недостатки:

    • Отсутствие конструктора: Невозможно использовать стандартный конструктор запросов.
    • Сложность отладки: При ручной сборке запроса легко допустить синтаксические ошибки, которые трудно отловить.
    • Меньшая читаемость: Для сложных запросов текст может стать нечитаемым.
  3. Использование параметров запроса

    Параметры запроса – это краеугольный камень безопасной и эффективной работы с данными в 1С. Они позволяют нам передавать значения в запрос, не изменяя его текст. Параметры обеспечивают защиту от SQL-инъекций, повышают читаемость кода и позволяют платформе кэшировать планы выполнения запросов.

    Пример установки параметра:

    Мы уже видели примеры использования параметров в предыдущих методах, но стоит подчеркнуть их важность. Для передачи одного значения:

    
    Запрос.УстановитьПараметр("Город", Города);
    

    Для передачи списка значений (для оператора `В`):

    
    СписокГородов = Новый СписокЗначений;
    СписокГородов.Добавить(Справочники.Города.Москва);
    СписокГородов.Добавить(Справочники.Города.СанктПетербург);
    Запрос.УстановитьПараметр("Города", СписокГородов);
    

    И затем в запросе:

    
    ...ГДЕ Субконто3.Город В (&Города)
    

    Преимущества:

    • Безопасность: Защита от SQL-инъекций.
    • Производительность: Платформа может кэшировать планы запросов.
    • Читаемость: Разделение текста запроса от передаваемых значений.
    • Гибкость: Возможность передавать различные типы данных, включая массивы и списки значений.
  4. Построитель запроса

    Объект ПостроительЗапроса – это мощный инструмент для программного конструирования запросов. Он позволяет нам динамически управлять не только условиями, но и составом полей, таблицами, связями, группировками и упорядочиванием. Построитель запроса особенно полезен для формирования отчетов и списков, где требуется высокая степень кастомизации.

    Мы можем добавлять условия, поля, накладывать отборы и устанавливать параметры через свойства и методы ПостроителяЗапроса. Например, для добавления условия по городу:

    
    Построитель = Новый ПостроительЗапроса;
    // ... устанавливаем источники данных, поля и т.д.
    
    Если ПоискПоГородам И ЗначениеЗаполнено(Города) Тогда
        ЭлементОтбора = Построитель.Отбор.Добавить("Город");
        ЭлементОтбора.ВидСравнения = ВидСравнения.Равно;
        ЭлементОтбора.Значение = Города;
        ЭлементОтбора.Использование = Истина;
    КонецЕсли;
    
    Запрос = Построитель.ПолучитьЗапрос();
    

    Использование построителя запроса сохраняет возможность работы с конструктором запросов 1С, так как он фактически строит запрос, который можно потом проанализировать. Это удобно для отладки сложных запросов.

  5. Система Компоновки Данных (СКД)

    СКД – это самый высокоуровневый и мощный механизм для построения отчетов в 1С. Она позволяет нам создавать сложные отчеты с динамическими отборами, группировками, упорядочиванием и оформлением, при этом большая часть логики описывается декларативно (в схеме компоновки данных), а не программно.

    В СКД мы можем использовать параметры и гибко управлять их выводом на форму и поведением. Динамические условия в СКД могут быть реализованы через параметры отбора, через программное изменение настроек компоновщика или даже через условные выражения в запросе, который является основой СКД. Например, для динамических отборов в СКД существуют специальные возможности, позволяющие задавать сложные группы условий с операторами "И", "ИЛИ", "НЕ".

    Хотя СКД является мощным инструментом, ее изучение требует времени. Однако в долгосрочной перспективе, особенно для разработки сложных аналитических отчетов, это наиболее эффективное решение.

Заключение и рекомендации

Выбор метода динамического формирования запросов зависит от сложности задачи и требуемой гибкости. Мы проанализировали несколько подходов, каждый из которых имеет свою область применения:

При переходе с других языков программирования, таких как C#, на 1С, важно помнить, что 1С акцентирует внимание на работе с метаданными и декларативном описании логики, а не на прямом манипулировании SQL-запросами. Изучение специфики языка запросов 1С, СКД и построителя запросов поможет вам быстро адаптироваться и создавать эффективные решения.

Всегда помните об оптимизации запросов. Избегайте "утяжеляющих" конструкций (РАЗЛИЧНЫЕ, СГРУППИРОВАТЬ ПО, сложные ВЫБОР в ГДЕ или условиях соединения) в динамических списках, чтобы не снижать производительность. Используйте консоль запросов 1С для отладки и проверки динамически формируемых запросов. Выбирайте тот способ, который наиболее прост и понятен для конкретной задачи и удобен для последующей поддержки.

← На главную