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