Отборы на формах списков в 1С:Предприятие 8.3 — это инструмент, который позволяет пользователям быстро фильтровать данные без написания запросов. В обычных формах (в отличие от управляемых) реализация отборов имеет свои особенности: здесь нет встроенного механизма Отбор как в управляемых формах, поэтому разработчику приходится создавать его вручную. Эта статья поможет разобраться, как правильно добавить отбор на форму списка, избегая типичных ошибок и используя проверенные методы.
Многие начинающие программисты 1С сталкиваются с проблемой: отбор либо не применяется, либо работает некорректно, либо тормозит интерфейс. Причины кроются в неправильной привязке к данным, отсутствии обработчиков событий или неоптимальных запросах. Мы рассмотрим 4 способа добавления отборов — от простейшего (через параметры формы) до продвинутого (динамический отбор с сохранением настроек), а также разберём, как избежать распространённых ошибок.
Если вы работаете с управляемыми формами, механизм отборов там реализован иначе — через свойство Отбор и стандартные элементы формы. Эта статья посвящена исключительно обычным формам, где всё приходится делать самостоятельно.
1. Способ 1: Отбор через параметры формы (простейший вариант)
Самый быстрый способ добавить отбор — использовать параметры формы. Этот метод подходит, если отбор статичный (например, всегда фильтруем документы за текущий месяц) или передаётся извне (например, при открытии формы из другого окна).
Для этого:
- Откройте форму списка в конфигураторе (например, форму списка документов
РеализацияТоваровУслуг). - В дереве элементов формы найдите корневой элемент
Формаи перейдите на вкладкуПараметры. - Добавьте новый параметр (например,
ДатаНачалаиДатаОкончания) с типомДата. - В модуле формы (в обработчике
ПриСозданииНаСервере) добавьте код применения отбора к основному запросу.
Пример кода для применения отбора по дате:
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если ЗначениеЗаполнено(Параметры.ДатаНачала) Или ЗначениеЗаполнено(Параметры.ДатаОкончания) Тогда
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТоваровУслуг.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
| РеализацияТоваровУслуг.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания";
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(Параметры.ДатаНачала));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(Параметры.ДатаОкончания));
Результат = Запрос.Выполнить();
Список = Результат.Выгрузить();
ЭлементыФормы.СписокДокументов.Список = Список;
КонецЕсли;
КонецПроцедуры
⚠️ Внимание: Этот способ перезагружает весь список при каждом открытии формы. Если данных много, лучше использовать динамический отбор (см. способ 3).
ОткрытьФорму("Документ.РеализацияТоваровУслуг.ФормаСписка", ,
, , , , Новый Структура("ДатаНачала, ДатаОкончания", НачалоМесяца(ТекущаяДата()), ТекущаяДата()));
-->
2. Способ 2: Отбор через элементы формы (пользовательский ввод)
Если нужно дать пользователю возможность самому задавать критерии отбора, добавьте на форму элементы ввода (поля для дат, выпадающие списки, флажки) и кнопку Применить отбор.
Алгоритм действий:
- Добавьте на форму элементы для ввода параметров отбора (например,
ПолеДатыНачала,ПолеДатыОкончания,ПолеКонтрагента). - Создайте кнопку
ПрименитьОтбори напишите для неё обработчик на сервере. - В обработчике сформируйте запрос с учётом заданных пользователем параметров.
Пример кода для обработчика кнопки:
Процедура ПрименитьОтбор(Кнопка)
Запрос = Новый Запрос;
ТекстЗапроса =
"ВЫБРАТЬ
| РеализацияТоваровУслуг.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
| РеализацияТоваровУслуг.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| " + ?(ЗначениеЗаполнено(ЭлементыФормы.ПолеКонтрагента.Значение),
| "И РеализацияТоваровУслуг.Контрагент = &Контрагент", "");
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ЭлементыФормы.ПолеДатыНачала.Значение));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ЭлементыФормы.ПолеДатыОкончания.Значение));
Если ЗначениеЗаполнено(ЭлементыФормы.ПолеКонтрагента.Значение) Тогда
Запрос.УстановитьПараметр("Контрагент", ЭлементыФормы.ПолеКонтрагента.Значение);
КонецЕсли;
Результат = Запрос.Выполнить();
ЭлементыФормы.СписокДокументов.Список = Результат.Выгрузить();
КонецПроцедуры
⚠️ Внимание: Если пользователь не заполнит поле ПолеКонтрагента, запрос вернёт ошибку из-за незаполненного параметра. Всегда проверяйте заполненность полей с помощью ЗначениеЗаполнено().
Добавить поля ввода на форму (даты, справочники, флажки)|Создать кнопку "Применить отбор"|Написать обработчик кнопки на сервере|Проверить обработку пустых значений в запросе|Оптимизировать запрос для больших объёмов данных
-->
3. Способ 3: Динамический отбор с сохранением настроек
Если отбор должен сохраняться между сеансами или автоматически применяться при открытии формы, используйте хранилище настроек. Этот способ подходит для часто используемых фильтров (например, "мои документы", "актуальные заказы").
Шаги реализации:
- Создайте в конфигураторе хранилище настроек (объект
ХранилищеНастроек) для формы. - В модуле формы добавьте обработчики
ПриОткрытии(загрузка сохранённых настроек) иПриЗакрытии(сохранение текущих настроек). - Реализуйте применение отбора при изменении параметров (без кнопки).
Пример кода для работы с хранилищем:
Перем мХранилищеНастроек;
// При открытии формы
Процедура ПриОткрытии(Отказ)
мХранилищеНастроек = Новый ХранилищеНастроек(ИмяФормы(), ИмяПользователя());
Если мХранилищеНастроек.Загрузить() Тогда
ЭлементыФормы.ПолеДатыНачала.Значение = мХранилищеНастроек.Получить("ДатаНачала");
ЭлементыФормы.ПолеДатыОкончания.Значение = мХранилищеНастроек.Получить("ДатаОкончания");
ПрименитьОтбор();
КонецЕсли;
КонецПроцедуры
// При закрытии формы
Процедура ПриЗакрытии(Отказ)
мХранилищеНастроек.Установить("ДатаНачала", ЭлементыФормы.ПолеДатыНачала.Значение);
мХранилищеНастроек.Установить("ДатаОкончания", ЭлементыФормы.ПолеДатыОкончания.Значение);
мХранилищеНастроек.Сохранить();
КонецПроцедуры
// При изменении параметров (например, даты)
Процедура ПолеДатыНачалаПриИзменении(Элемент)
ПрименитьОтбор();
КонецПроцедуры
Критическая деталь: хранилище настроек привязано к имени пользователя. Если форма открывается разными пользователями, каждый будет видеть свои настройки отбора.
Чтобы сбросить настройки, достаточно удалить файл хранилища из каталога Как очистить сохранённые настройки отбора?
%APPDATA%\1C\1Cv8\Настройки\ или программно вызвать метод мХранилищеНастроек.Очистить() перед сохранением.
4. Способ 4: Отбор через временные таблицы (для сложных фильтров)
Если отбор включает сложные условия (например, фильтрацию по нескольким связанным справочникам или динамические расчёты), используйте временные таблицы. Это ускорит работу формы и уменьшит нагрузку на сервер.
Пример реализации:
- Создайте временную таблицу с нужными данными при открытии формы.
- Применяйте отбор к этой таблице, а не к основному источнику данных.
- Обновляйте таблицу только при изменении параметров отбора.
Код для работы с временной таблицей:
Перем мТаблицаДанных;
// При открытии формы
Процедура ПриОткрытии(Отказ)
ОбновитьВременнуюТаблицу();
КонецПроцедуры
// Обновление данных с учётом отбора
Процедура ОбновитьВременнуюТаблицу()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТоваровУслуг.Ссылка КАК Ссылка,
| РеализацияТоваровУслуг.Дата КАК Дата,
| РеализацияТоваровУслуг.СуммаДокумента КАК Сумма
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
| РеализацияТоваровУслуг.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания";
Запрос.УстановитьПараметр("ДатаНачала", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаОкончания", ТекущаяДата());
мТаблицаДанных = Запрос.Выполнить().Выгрузить();
ЭлементыФормы.СписокДокументов.Список = мТаблицаДанных;
КонецПроцедуры
// При изменении параметров отбора
Процедура ПолеДатыНачалаПриИзменении(Элемент)
ОбновитьВременнуюТаблицу();
КонецПроцедуры
⚠️ Внимание: Временные таблицы занимают оперативную память. Если данных слишком много, разбейте их на страницы или используйте постраничную загрузку.
| Способ отбора | Сложность реализации | Подходит для | Минусы |
|---|---|---|---|
| Параметры формы | Низкая | Статичные отборы, передача параметров извне | Не сохраняет настройки, перезагружает данные |
| Элементы формы + кнопка | Средняя | Пользовательский ввод, простые фильтры | Требует ручного применения отбора |
| Динамический отбор с хранилищем | Высокая | Часто используемые фильтры, сохранение между сеансами | Сложнее в отладке, привязка к пользователю |
| Временные таблицы | Очень высокая | Сложные фильтры, большие объёмы данных | Потребляет память, требует оптимизации |
5. Типичные ошибки и как их избежать
Даже опытные разработчики 1С иногда допускают ошибки при настройке отборов. Вот самые распространённые из них и способы их решения:
- 🔴 Отбор не применяется: Проверьте, что обработчик кнопки или события
ПриИзменениидействительно вызывается. ДобавьтеСообщить("Тест")в начало процедуры для отладки. - 🔴 Запрос возвращает пустой результат: Убедитесь, что параметры запроса передаются корректно. Используйте
Запрос.ТекстЗапросаДляОтладки(), чтобы увидеть финальный SQL. - 🔴 Форма тормозит: Оптимизируйте запрос (добавьте индексы, уменьшите количество полей в выборке) или используйте постраничную загрузку.
- 🔴 Настройки отбора не сохраняются: Проверьте, что хранилище настроек привязано к правильному имени формы и пользователя.
⚠️ Внимание: Если в отборе используются динамические списки (например, справочник контрагентов), убедитесь, что они обновляются при изменении данных в базе. Иначе пользователь может не увидеть новые элементы.
Всегда тестируйте отборы на больших объёмах данных. То, что работает быстро на 100 документах, может "подвесить" форму на 10 000 записях.
6. Продвинутые техники: отбор с использованием SQL и полнотекстового поиска
Для ускорения работы с большими данными можно использовать прямые SQL-запросы или полнотекстовый поиск. Это актуально, если стандартные механизмы 1С работают слишком медленно.
Пример SQL-отбора (только для опытных разработчиков!):
Процедура ПрименитьSQLОтбор()
ТекстЗапроса =
"SELECT
| T1._IDRRef AS Ссылка
|FROM
| _Document123 AS T1 (NOLOCK)
|WHERE
| T1._Date BETWEEN ? AND ?
| AND T1._Fld12456 = ?"; // _Fld12456 — внутренний идентификатор реквизита "Контрагент"
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр(1, НачалоДня(ЭлементыФормы.ПолеДатыНачала.Значение));
Запрос.УстановитьПараметр(2, КонецДня(ЭлементыФормы.ПолеДатыОкончания.Значение));
Запрос.УстановитьПараметр(3, ЭлементыФормы.ПолеКонтрагента.Значение.УникальныйИдентификатор());
Результат = Запрос.Выполнить();
// Преобразование результата в таблицу значений для отображения
ТаблицаРезультатов = Результат.Выгрузить();
ЭлементыФормы.СписокДокументов.Список = ТаблицаРезультатов;
КонецПроцедуры
⚠️ Внимание: Прямые SQL-запросы непереносимы между разными СУБД (MS SQL, PostgreSQL, FileDB). Используйте их только если другие методы не дают приемлемой производительности.
Для полнотекстового поиска (например, поиск по наименованию или комментарию) используйте оператор ПОДОБНО или функции СтрНайти:
Процедура ПоискПоНаименованию(СтрокаПоиска)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование ПОДОБНО &Поиск";
Запрос.УстановитьПараметр("Поиск", "%" + СтрокаПоиска + "%");
Результат = Запрос.Выполнить();
ЭлементыФормы.СписокНоменклатуры.Список = Результат.Выгрузить();
КонецПроцедуры
7. Оптимизация производительности отборов
Отборы могут значительно замедлять работу формы, если не оптимизированы. Вот ключевые рекомендации:
- ⚡ Используйте индексы: Убедитесь, что поля, по которым идёт отбор, проиндексированы в базе данных. Например, для отбора по дате должно быть индексировано поле
Дата. - ⚡ Ограничивайте выборку: Запрашивайте только те поля, которые действительно нужны для отображения (избегайте
ВЫБРАТЬ *). - ⚡ Применяйте постраничную загрузку: Для больших списков используйте механизм
ПозицияНачальнаяиРазмерПачки. - ⚡ Кэшируйте результаты: Если данные редко меняются, сохраняйте результаты отбора в временной таблице и обновляйте её только при изменении параметров.
Пример постраничной загрузки:
Процедура ЗагрузитьДанныеСтранично(НомерСтраницы, РазмерСтраницы)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ " + РазмерСтраницы + "
| РеализацияТоваровУслуг.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
| РеализацияТоваровУслуг.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
|УПОРЯДОЧИТЬ ПО
| РеализацияТоваровУслуг.Дата УБЫВ
|НАЧИНАЯ С " + (НомерСтраницы - 1) * РазмерСтраницы;
// ... остальной код
КонецПроцедуры
Критическая деталь: при постраничной загрузке не забывайте сортировать данные по уникальному полю (например, Ссылка или Дата), иначе при изменении данных могут появиться дубли или пропуски.
FAQ: Ответы на частые вопросы
Как сделать отбор по нескольким значениям справочника (например, список контрагентов)?
Используйте оператор В в запросе:
ГДЕ
| РеализацияТоваровУслуг.Контрагент В (&СписокКонтрагентов)
Где &СписокКонтрагентов — массив ссылок на элементы справочника. Чтобы пользователь мог выбрать несколько значений, добавьте на форму элемент ПолеВыбораСписка с свойством МножественныйВыбор = Истина.
Можно ли сделать отбор по динамическому списку (например, только "мои документы")?
Да. Для этого в отбор добавьте условие по текущему пользователю:
ГДЕ
| РеализацияТоваровУслуг.Ответственный = &ТекущийПользователь
Где &ТекущийПользователь — это ПользователиИнформационнойБазы.ТекущийПользователь().
Почему отбор работает медленно, хотя данных мало?
Возможные причины:
- Отсутствуют индексы на полях, по которым идёт отбор.
- В запросе используются функции (например,
НАЧАЛОПЕРИОДА()), которые не могут использовать индексы. - Запрос выбирает слишком много полей или связанных таблиц.
Решение: проверьте план выполнения запроса в SQL Profiler или используйте ОбъяснитьЗапрос() в 1С.
Как сбросить все отборы на форме?
Добавьте кнопку СброситьОтбор и в её обработчике:
- Очистите все поля ввода (даты, справочники и т.д.).
- Выполните запрос без условий
ГДЕ. - Если используете хранилище настроек, очистите его (
мХранилищеНастроек.Очистить()).
Можно ли сделать отбор по произвольному полю, которого нет в форме?
Да, но для этого придётся:
- Добавить скрытое поле на форму (например,
ПолеПроизвольногоОтбора). - В обработчике отбора динамически формировать текст запроса с учётом этого поля.
- Использовать
ВычислитьВыражение(), если нужно отфильтровать по расчётному полю.
Пример:
ТекстЗапроса = "ВЫБРАТЬ ... ГДЕ " + ?(Не ПустаяСтрока(ПроизвольноеУсловие),
| ПроизвольноеУсловие, "1=1");