Как реализовать динамический отбор в запросе 1С, если параметры в шапке документа могут быть пустыми?

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

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

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

Рассмотрим стандартную ситуацию: в документе ВводНормДляСотрудников есть реквизиты Подразделение, Должность, Звание и другие. Если мы напишем обычный статический запрос с жестким условием ГДЕ Подразделение = &Подразделение, то при незаполненном подразделении запрос будет искать сотрудников с пустым подразделением, а не всех сотрудников, как того ожидает пользователь. Наша цель — сделать так, чтобы условие добавлялось в запрос только тогда, когда параметр действительно задан.

Решение 1: Динамическая сборка текста запроса (Конкатенация строк)

Это самый популярный и интуитивно понятный способ. Мы анализируем каждый реквизит шапки и, если он заполнен, добавляем фрагмент текста в секцию ГДЕ. Проанализируем правильный алгоритм реализации этого метода.

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


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

Важный нюанс: Обратите внимание на использование оператора ?() (тернарный оператор) для добавления слова И. Если это первое условие в списке, И не добавляется. Это избавляет от ошибок синтаксиса, на которые часто натыкаются начинающие разработчики.

Решение 2: Использование универсального шаблона (ЛОГИЧЕСКОЕ ИЛИ)

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


Запрос.Текст = 
"ВЫБРАТЬ
|	Сотрудники.Ссылка КАК Сотрудник
|ИЗ
|	Справочник.Сотрудники КАК Сотрудники
|ГДЕ
|	(&ОтборПоПодразделению = ЛОЖЬ ИЛИ Сотрудники.Подразделение = &Подразделение)
|	И (&ОтборПоЗванию = ЛОЖЬ ИЛИ Сотрудники.Звание = &Звание)";

Запрос.УстановитьПараметр("Подразделение", Объект.Подразделение);
Запрос.УстановитьПараметр("ОтборПоПодразделению", ЗначениеЗаполнено(Объект.Подразделение));

Запрос.УстановитьПараметр("Звание", Объект.Звание);
Запрос.УстановитьПараметр("ОтборПоЗванию", ЗначениеЗаполнено(Объект.Звание));

В данном случае, если Объект.Подразделение пустое, параметр &ОтборПоПодразделению примет значение Ложь. Условие в скобках станет истинным для всех записей, и фильтрация по этому полю фактически отключится. Этот метод делает код более читаемым и легким для отладки в консоли запросов.

Решение 3: Программная работа через "Схему запроса"

Для сложных динамических запросов в платформе 1С предусмотрен объект СхемаЗапроса. Он позволяет программно управлять структурой запроса (полями, источниками, условиями) без манипуляций со строками. Посмотрим на пример использования:


Схема = Новый СхемаЗапроса;
Схема.УстановитьТекстЗапроса("ВЫБРАТЬ Сотрудники.Ссылка ИЗ Справочник.Сотрудники КАК Сотрудники");
Оператор = Схема.ПакетЗапросов[0].Операторы[0];

Если ЗначениеЗаполнено(Объект.Подразделение) Тогда
    Оператор.Отбор.Добавить("Сотрудники.Подразделение = &Подразделение");
    Запрос.УстановитьПараметр("Подразделение", Объект.Подразделение);
КонецЕсли;

Запрос.Текст = Схема.ПолучитьТекстЗапроса();

Метод Оператор.Отбор.Добавить() автоматически добавляет ГДЕ и расставляет И между условиями. Это наиболее надежный способ с точки зрения предотвращения синтаксических ошибок.

Оптимизация заполнения табличной части

После получения данных из запроса их нужно перенести в табличную часть (ТЧ). Проанализируем два подхода и выясним, какой из них эффективнее.

  1. Построчный обход (Медленно): Использование цикла Пока Выборка.Следующий() с методом Добавить(). При каждой итерации происходит обращение к серверу для отрисовки новой строки в интерфейсе, что замедляет работу при большом количестве сотрудников.
  2. Выгрузка в таблицу значений (Быстро): Мы можем выгрузить весь результат запроса в объект ТаблицаЗначений, а затем одной командой загрузить его в ТЧ.

Разберем пример оптимизированного заполнения:


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

Важно: Чтобы метод Загрузить() сработал корректно, имена колонок в результате запроса (через оператор КАК) должны полностью совпадать с именами реквизитов вашей табличной части.

Профессиональные советы

Рассмотрим несколько дополнительных аспектов, которые помогут сделать ваше решение по-настоящему профессиональным:

Подводя итог, мы выяснили, что для решения задачи динамического отбора лучше всего использовать либо программную сборку текста с проверкой заполненности параметров, либо объект СхемаЗапроса. Это гарантирует гибкость интерфейса и корректность данных, которые увидит конечный пользователь.

← На главную