Установка отборов программно — одна из самых востребованных задач при разработке в 1С:Предприятие 8.3. Без грамотной фильтрации данных невозможно построить эффективные отчеты, динамические списки или управляемые формы. Однако многие разработчики сталкиваются с проблемами: отбор либо не применяется, либо работает некорректно, либо «съедает» производительность при больших объемах данных.

В этой статье мы разберем 5 проверенных способов программной установки отборов — от базовых методов для начинающих до оптимизированных решений для работы с большими массивами. Каждый метод сопровождается реальными примерами кода, которые можно сразу использовать в своих конфигурациях. Особое внимание уделим типичным ошибкам (например, почему отбор через Запрос.Текст может игнорироваться) и способам их обхода.

Материал будет полезен как новичкам, которые только осваивают встроенный язык , так и опытным программистам, ищущим оптимальные решения для сложных задач. Все примеры актуальны для платформы 1С:Предприятие 8.3 (включая последние релизы 2026 года) и совместимы с большинством типовых конфигураций (Бухгалтерия 3.0, УТ 11, ЗУП 3.1 и др.).

1. Установка отбора через свойство объекта (простой способ)

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

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

  • 🔹 Простота реализации — достаточно 1-2 строк кода.
  • 🔹 Визуальная наглядность: отборы отображаются в интерфейсе пользователя (если объект поддерживает управляемые формы).
  • 🔹 Автоматическая оптимизация — платформа сама выбирает оптимальный способ применения фильтра (индексы, кэширование и т.д.).

Пример установки отбора для списка документов РеализацияТоваровУслуг по дате и контрагенту:

Документы = Документы.РеализацияТоваровУслуг.СоздатьМенеджерВыбора();

Документы.Отбор.Дата.Установить(НачалоДня(ТекущаяДата()), КонецДня(ТекущаяДата()));

Документы.Отбор.Контрагент.Установить(Справочники.Контрагенты.НайтиПоНаименованию("ООО Ромашка"));

Выборка = Документы.Выбрать();

Пока Выборка.Следующий() Цикл

Сообщить(Выборка.Номер);

КонецЦикла;

⚠️ Внимание: Если вы устанавливаете отбор для ДинамическогоСписка в управляемой форме, не забудьте вызвать метод ПрименитьОтбор() или Обновить(), иначе изменения не отобразятся в интерфейсе.

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

Справочник = Справочники.Номенклатура.СоздатьМенеджерВыбора();

Справочник.Отбор.Родитель.Установить(Справочники.Номенклатура.ПустаяСсылка()); // Только элементы верхнего уровня

Справочник.Отбор.ЭтоГруппа.Установить(Ложь); // Исключаем группы

💡

Если вам нужно сбросить все отборы для объекта, используйте метод ОчиститьОтбор() без параметров. Это полезно при динамическом изменении фильтров в форме.

2. Отбор в запросах: когда и как использовать

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

Основные правила для отборов в запросах:

  • 📌 Используйте параметры запроса (Параметры.Добавить()) вместо жесткого прописывания значений — это защищает от SQL-инъекций и упрощает поддержку кода.
  • 📌 Для дат всегда применяйте функции НАЧАЛОПЕРИОДА() или КОНЕЦПЕРИОДА(), чтобы избежать проблем с временем.
  • 📌 Сложные условия (с ИЛИ) лучше выносить в отдельные секции ГДЕ с круглыми скобками для явного указания приоритета.

Пример оптимизированного запроса с отбором по нескольким полям:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| РеализацияТоваровУслуг.Ссылка КАК Ссылка,

| РеализацияТоваровУслуг.Дата КАК Дата,

| РеализацияТоваровУслуг.СуммаДокумента КАК Сумма

|ИЗ

| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

|ГДЕ

| РеализацияТоваровУслуг.Дата МЕЖДУ &НачалоПериода И &КонецПериода

| И РеализацияТоваровУслуг.Контрагент В (&СписокКонтрагентов)

| И РеализацияТоваровУслуг.ПометкаУдаления = ЛОЖЬ";

Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ТекущаяДата() - 30));

Запрос.УстановитьПараметр("КонецПериода", КонецДня(ТекущаяДата()));

Запрос.УстановитьПараметр("СписокКонтрагентов", МассивКонтрагентов); // Массив ссылок

Результат = Запрос.Выполнить();

Для динамического формирования условия ГДЕ можно использовать конструктор запросов:

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ | ......"; // Базовая часть

Условие = Новый Структура("Тип, Значение", "Даты", "РеализацияТоваровУслуг.Дата МЕЖДУ &Начало И &Конец");

Если НЕ ПустаяДата(ДатаНачала) Тогда

Запрос.Текст = Запрос.Текст + " |ГДЕ " + Условие.Значение;

Запрос.УстановитьПараметр("Начало", НачалоДня(ДатаНачала));

Запрос.УстановитьПараметр("Конец", КонецДня(ДатаКонца));

КонецЕсли;

📊 Какой способ установки отборов вы используете чаще?
Через свойство Отбор
В тексте запроса
Динамические списки
Комбинация методов

3. Динамические списки: отборы с привязкой к форме

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

Особенности работы с отборами в динамических списках:

  • 🔄 Отборы применяются на сервере, что снижает нагрузку на клиент.
  • 🔄 Поддерживается иерархическая фильтрация (например, для справочников с группами).
  • 🔄 Можно комбинировать с ОтборПоумолчанию из свойств формы.

Пример программной установки отбора для динамического списка в форме документа:

&НаСервере

Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

Отбор = Новый Структура;

Отбор.Вставить("Дата", Новый ЗначениеВДиапазоне(НачалоДня(ТекущаяДата()), КонецДня(ТекущаяДата())));

Отбор.Вставить("Контрагент", Справочники.Контрагенты.ТекущийЭлемент());

ЭлементыФормы.СписокДокументов.Отбор = Отбор;

ЭлементыФормы.СписокДокументов.ПрименитьОтбор();

КонецПроцедуры

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

&НаСервере

Процедура СписокДокументовПередЗагрузкойДанных(Элемент, ПараметрыЗагрузки)

Если ЭлементыФормы.ФильтрПоДате.Значение Тогда

ПараметрыЗагрузки.Отбор.Дата = Новый ЗначениеВДиапазоне(

ЭлементыФормы.ДатаНачала.Значение,

ЭлементыФормы.ДатаОкончания.Значение

);

КонецЕсли;

КонецПроцедуры

⚠️ Внимание: Если динамический список привязан к регистру сведений или регистру накопления, отбор по измерениям может существенно влиять на производительность. В таких случаях проверяйте план запроса через ОбъяснитьЗапрос().

Убедиться, что отбор установлен на сервере|Проверить вызов ПрименитьОтбор() после изменения|Тестировать с разными правами пользователей|Оптимизировать отборы для больших объемов данных-->

4. Отборы в отчетах и система компоновки данных (СКД)

Система компоновки данных (СКД) предоставляет гибкие инструменты для установки отборов, включая:

  • 📊 Статические отборы (задаются в схеме компоновки).
  • 📊 Динамические отборы (устанавливаются программно).
  • 📊 Пользовательские настройки (сохраняются между сеансами).

Для программной установки отбора в СКД используйте свойство Настройки.Отбор:

Отчет = Отчеты.Продажи.Создать();

СхемаКомпоновки = Отчет.КомпоновщикНастроек.ПолучитьНастройкиПоУмолчанию();

ОтборЭлемент = СхемаКомпоновки.Отбор.Элементы.Добавить(Тип("ОтборКомпоновкиДанныхЭлемент"));

ОтборЭлемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Дата");

ОтборЭлемент.ВидСравнения = ВидСравненияКомпоновкиДанных.БольшеИлиРавно;

ОтборЭлемент.ПравоеЗначение = НачалоДня(ТекущаяДата() - 30);

Отчет.КомпоновщикНастроек.ЗагрузитьНастройки(СхемаКомпоновки);

Отчет.СкомпоноватьРезультат();

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

Настройки = Отчет.КомпоновщикНастроек.ПолучитьНастройки();

Отчет.КомпоновщикНастроек.ЗагрузитьПользовательскиеНастройки(Настройки, ИмяПользователя, ИмяВарианта);

Отбор = Настройки.Отбор.Элементы.Найти(Новый ПолеКомпоновкиДанных("Контрагент"));

Если Отбор <> Неопределено Тогда

Отбор.ПравоеЗначение = Справочники.Контрагенты.ТекущийЭлемент();

КонецЕсли;

Если отчет использует параметры, их можно привязать к отборам через свойство ПараметрыКомпоновкиДанных:

Параметр = СхемаКомпоновки.ПараметрыДанных.Элементы.Добавить(Тип("ПараметрКомпоновкиДанныхЭлемент"));

Параметр.Имя = "Период";

Параметр.Значение = Новый Структура("Начало, Конец", НачалоМесяца(ТекущаяДата()), КонецМесяца(ТекущаяДата()));

ОтборЭлемент.ПравоеЗначение = Новый ПолеКомпоновкиДанных("Параметр.Период.Начало");

5. Оптимизация отборов для больших баз данных

При работе с большими объемами данных (десятки тысяч записей и более) неоптимальные отборы могут приводить к «зависанию» системы. Рассмотрим ключевые приемы оптимизации:

Проблема Решение Пример кода
Полное сканирование таблиц Использовать индексированные поля в отборе Отбор.Дата = ТекущаяДата() (поле Дата должно быть индексировано)
Сложные условия с ИЛИ Разбивать на несколько запросов с ОБЪЕДИНИТЬ
Запрос1.Текст = "ВЫБРАТЬ ... ГДЕ Поле1 = Значение1";

Запрос2.Текст = "ВЫБРАТЬ ... ГДЕ Поле2 = Значение2";

Результат = Запрос1.Выполнить().Объединить(Запрос2.Выполнить());

Отбор по неиндексированному полю Добавить поле в индекс или использовать временные таблицы
ВТ = Новый ВременнаяТаблица;

ВТ.ДобавитьКолонку("Ссылка");

ВТ.ДобавитьКолонку("НеиндексПоле");

Для анализа производительности используйте метод ОбъяснитьЗапрос():

Запрос = Новый Запрос("ВЫБРАТЬ ... ГДЕ ...");

Объяснение = Запрос.ОбъяснитьЗапрос();

Сообщить(Объяснение.Текст); // Показывает план выполнения

Если запрос выполняется слишком долго, рассмотрите альтернативные подходы:

  • 🔧 Виртуальные таблицы — для работы с регистрами накопления.
  • 🔧 Объектные выборки — если нужно получить только ссылки на объекты.
  • 🔧 Пакетные запросы — для обработки данных порциями.
Как ускорить отбор по неиндексированному полю?

Если поле не входит в индекс, но отбор по нему критичен, можно:

1. Добавить поле в индекс (если это возможно по логике данных).

2. Использовать временную таблицу с предварительной выборкой:

ВТ = Новый ВременнаяТаблица;

Запрос = Новый Запрос("ВЫГРУЗИТЬ ВТ ИЗ ВЫБРАТЬ РазрезыПоследних.Ссылка, РазрезыПоследних.НеиндексПоле ИЗ РегистрСведений.РазрезыПоследних КАК РазрезыПоследних");

Запрос.Выполнить();

3. Применить отбор уже к временной таблице.

6. Типичные ошибки и как их избежать

Даже опытные разработчики иногда допускают ошибки при работе с отборами. Рассмотрим самые распространенные:

  1. Отбор не применяется.

    Причина: забыли вызвать ПрименитьОтбор() для динамического списка или не обновили форму.

    Решение: всегда проверяйте, что после установки отбора вызывается метод обновления.

  2. Некорректная работа с датами.

    Причина: не учтено время (например, отбор по Дата = ТекущаяДата() может не включать документы с временем 00:00:01).

    Решение: используйте НачалоДня()/КонецДня() или диапазоны.

  3. Ошибки при работе с NULL.

    Причина: отбор по полю со значением NULL (например, Контрагент = NULL) работает не так, как ожидается.

    Решение: используйте ЗначениеЗаполнено() или ЕСТЬ NULL в запросах.

Пример корректной работы с NULL:

// Неправильно (не вернет записи с пустым контрагентом):

Отбор.Контрагент.Установить(Неопределено);

// Правильно:

Отбор.Контрагент.Установить(Справочники.Контрагенты.ПустаяСсылка());

// В запросе:

ГДЕ Контрагент ЕСТЬ NULL

Еще одна типичная проблема — конфликт отборов, когда несколько условий противоречат друг другу. Например:

Отбор.Дата.Установить(НачалоДня(ТекущаяДата()), КонецДня(ТекущаяДата()));

Отбор.Дата.Установить(НачалоМесяца(ТекущаяДата())); // Перезапишет предыдущий отбор!

Чтобы избежать таких ситуаций, используйте ДобавитьУсловиеОтбора() для динамических списков или объединяйте условия в запросе через И:

ГДЕ

Дата МЕЖДУ &Начало И &Конец

И Контрагент = &Контрагент

💡

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

FAQ: Частые вопросы по программной установке отборов

Как установить отбор по нескольким значениям одного поля (например, список контрагентов)?

Для отбора по нескольким значениям используйте:

  • В запросах: оператор В (&МассивЗначений).
  • В динамических списках: Отбор.Поле.Установить(МассивЗначений).

Пример для запроса:

Запрос.Текст = "ВЫБРАТЬ ... ГДЕ Контрагент В (&СписокКонтрагентов)";

Запрос.УстановитьПараметр("СписокКонтрагентов", МассивКонтрагентов);

Можно ли установить отбор по полю, которого нет в основной таблице (например, по реквизиту табличной части)?

Да, но нужно использовать вложенные запросы или соединения таблиц.

Пример для табличной части документа:

ВЫБРАТЬ

РеализацияТоваровУслуг.Ссылка

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

ГДЕ

СУЩЕСТВУЕТ (

ВЫБРАТЬ РАЗЛИЧНЫЕ 1

ИЗ Документ.РеализацияТоваровУслуг.Товары КАК Товары

ГДЕ Товары.Ссылка = РеализацияТоваровУслуг.Ссылка

И Товары.Номенклатура = &Номенклатура

)

Почему отбор в динамическом списке не работает после обновления платформы?

В новых версиях 1С:Предприятие 8.3 (начиная с 8.3.20+) изменился механизм применения отборов для некоторых объектов. Проверьте:

  1. Вызывается ли ПрименитьОтбор() после установки всех условий.
  2. Не конфликтуют ли программные отборы с настройками формы (проверьте свойство ОтборПоумолчанию).
  3. Обновлены ли метаданные конфигурации (иногда требуется пересоздать динамический список).

Если проблема сохраняется, используйте Обновить() для принудительного пересчета:

ЭлементыФормы.СписокДокументов.Отбор.Очистить();

ЭлементыФормы.СписокДокументов.ПрименитьОтбор();

ЭлементыФормы.СписокДокументов.Обновить();

Как сделать отбор по иерархическому справочнику (включая подчиненные элементы)?

Для иерархических справочников используйте функцию ПолучитьСсылкиПотомков() или оператор ПОДЧИНЕНО в запросах.

Пример для программного отбора:

Родитель = Справочники.Номенклатура.НайтиПоНаименованию("Товары");

СписокПотомков = Родитель.ПолучитьСсылкиПотомков();

Отбор = Новый Структура("Поле", СписокПотомков);

ЭлементыФормы.СписокНоменклатуры.Отбор.Ссылка = Отбор;

В запросе:

ГДЕ Номенклатура ПОДЧИНЕНО &Родитель
Можно ли сохранить пользовательские отборы между сеансами?

Да, для этого используйте:

  • Для динамических списков: свойство ПользовательскиеНастройки формы.
  • Для отчетов СКД: метод СохранитьПользовательскиеНастройки().

Пример сохранения отбора в форме:

&НаКлиенте

Процедура СохранитьНастройки(Команда)

Настройки = ЭлементыФормы.СписокДокументов.ПолучитьНастройки();

ПользовательскиеНастройки.СохранитьНастройки(Настройки, ИмяПользователя);

КонецПроцедуры