К статье
Практический материал

Разработка и Конфигурация · 13 мин

1С: как получить строки, отобранные отбором

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

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

Ниже разобраны основные сценарии: чтение установленного отбора из динамического списка формы, получение строк табличного поля с учётом отбора, формирование запроса с теми же условиями и перебор результата. Примеры приведены на встроенном языке 1С и подходят для управляемых форм типовых конфигураций (УТ, ERP, БП), а также самописных решений.

Важно понимать с самого начала: платформа не хранит «готовый список отобранных строк» где-то отдельно. Отбор — это условие, которое применяется к данным при чтении. Поэтому правильный подход почти всегда сводится к тому, чтобы взять параметры отбора и повторно выполнить выборку с этими условиями на сервере.

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

Динамический список на управляемой форме — это не таблица значений в памяти, а описание выборки. Платформа подгружает данные порциями по мере прокрутки, поэтому «все строки, которые видит пользователь», физически на клиенте не существуют. Попытка обойти строки табличного поля даст либо только текущую порцию, либо вообще ничего полезного.

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

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

Читаем установленный отбор программно

Первый шаг — получить элементы отбора, которые реально действуют на список. Для формы списка с основным реквизитом Список типа «ДинамическийСписок» доступ к настройкам выглядит так:

Настройки = Список.КомпоновщикНастроек.Настройки;

Для Каждого ЭлементОтбора Из Настройки.Отбор.Элементы Цикл

Если ТипЗнч(ЭлементОтбора) = Тип("ЭлементОтбораКомпоновкиДанных")

И ЭлементОтбора.Использование Тогда

// ЭлементОтбора.ЛевоеЗначение — поле

// ЭлементОтбора.ВидСравнения — оператор

// ЭлементОтбора.ПравоеЗначение — значение

КонецЕсли;

КонецЦикла;

Обратите внимание на проверку Использование: элементы отбора могут существовать, но быть отключёнными, и учитывать их в выборке нельзя. Также отбор может содержать вложенные группы (ГруппаЭлементовОтбораКомпоновкиДанных) с условиями И/ИЛИ — для корректной обработки их нужно обходить рекурсивно.

Если пользователь задал отбор через строку поиска или стандартную панель, часть условий окажется в Список.Отбор. Проверить, что отбор вообще установлен, можно через Список.Отбор.Элементы.Количество() и аналогично для компоновщика.

Получаем строки повторным запросом с теми же условиями

Самый надёжный способ получить именно те строки, которые видит пользователь, — выполнить выборку на сервере с перенесённым отбором. Есть два рабочих варианта: собрать запрос вручную или переиспользовать схему компоновки динамического списка.

Вариант 1: запрос с параметрами

Подходит, когда отбор простой и поля известны заранее. Условия из отбора превращаются в параметры запроса:

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

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

"ВЫБРАТЬ

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

|ИЗ

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

|ГДЕ

| РеализацияТоваровУслуг.Организация = &Организация

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

Запрос.УстановитьПараметр("Организация", ОрганизацияИзОтбора);

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

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

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

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

// обработка Выборка.Ссылка

КонецЦикла;

Вариант 2: СКД с настройками списка

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

Схема = Новый СхемаКомпоновкиДанных;

Источник = Схема.ИсточникиДанных.Добавить();

Источник.Имя = "ИсточникДанных1";

Источник.ТипИсточникаДанных = "Local";

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

Набор.Запрос = Список.ТекстЗапроса; // запрос динамического списка

Набор.АвтоЗаполнениеДоступныхПолей = Истина;

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;

Макет = КомпоновщикМакета.Выполнить(Схема,

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

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;

ПроцессорКомпоновки.Инициализировать(Макет);

Результат = Новый ТаблицаЗначений;

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;

ПроцессорВывода.УстановитьОбъект(Результат);

ПроцессорВывода.Вывести(ПроцессорКомпоновки);

Такой подход гарантирует, что выборка совпадёт с тем, что видит пользователь, включая отборы по полям через точку и сложные группы условий. Плата за универсальность — больше кода и необходимость аккуратно оформить набор данных схемы.

bsl
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    ОбработатьСтроку(Выборка.Ссылка);
КонецЦикла;

Отбор строк в табличной части и таблице значений

Отдельный и более простой случай — отбор уже загруженных данных: табличная часть документа или реквизит формы типа «ТаблицаЗначений». Здесь строки реально существуют в памяти, и отобранные строки получают методом НайтиСтроки():

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

ОтборСтрок.Вставить("Склад", ВыбранныйСклад);

ОтборСтрок.Вставить("Отгружено", Ложь);

НайденныеСтроки = Объект.Товары.НайтиСтроки(ОтборСтрок);

Для Каждого СтрокаТовара Из НайденныеСтроки Цикл

// работаем со строкой

КонецЦикла;

Метод возвращает массив строк, полностью совпадающих по всем полям структуры (условия соединяются по И). Значения сравниваются на строгое равенство — никаких «содержит» и «больше/меньше» здесь нет. Для нестрогих условий придётся перебирать коллекцию в цикле с собственной проверкой.

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

Сравнение вариантов

НайтиСтроки() для табличной части

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

Запрос с параметрами

Подходит для отбора по базе данных. Гибкие условия, но отбор нужно переносить вручную.

СКД с настройками списка

Точно повторяет выборку динамического списка, включая сложные отборы пользователя. Требует больше кода и аккуратности.

Выделенные строки vs отобранные строки

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

Для Каждого Идентификатор Из Элементы.Список.ВыделенныеСтроки Цикл

ТекущиеДанные = Список.НайтиПоИдентификатору(Идентификатор);

// для динамического списка идентификатор часто сразу является ссылкой

КонецЦикла;

Для динамического списка элементы ВыделенныеСтроки обычно уже содержат ссылки на объекты, поэтому обход ещё проще. Но помните: выделение работает только с тем, что пользователь реально отметил, а отбор — со всей выборкой, даже если она на тысячи строк и не умещается на экране.

Маршрут решения

Как выбрать способ

Пользователь выделил несколько строк мышьюПеребирайте Элементы.Список.ВыделенныеСтроки — отбор тут ни при чём
Нужно обработать всё, что попало под отбор спискаЧитайте настройки компоновщика и выполняйте запрос или СКД на сервере
Отбор нужен по строкам табличной части документаИспользуйте НайтиСтроки() со структурой отбора
Условия сложные, с ИЛИ и полями через точкуПрименяйте СКД с настройками динамического списка, не собирайте запрос вручную

Производительность и безопасная обработка больших выборок

Если под отбор попадают тысячи документов, обработка «в лоб» в одной транзакции может надолго заблокировать базу и вызвать тайм-ауты у других пользователей. Соблюдайте несколько правил.

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

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

Типичные ошибки при работе с отобранными строками

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

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

Четвёртая группа ошибок связана с типами: отбор по строковому представлению вместо ссылки, сравнение дат без учёта времени (конец периода в 00:00:00 отсекает весь последний день), передача в НайтиСтроки() значения другого типа — строка не найдётся даже при визуальном совпадении.

Типичные ошибки
01
Перебор строк динамического списка на клиенте — обрабатывается только видимая порция или ничего
02
Игнорирование свойства Использование у элементов отбора — в выборку попадают лишние ограничения
03
Дата конца периода без времени 23:59:59 — теряются документы последнего дня
04
Сравнение в НайтиСтроки() значения другого типа — строки не находятся без видимой причины
05
Массовая обработка одной транзакцией — блокировки и тайм-ауты в многопользовательской базе

Проверка результата

После реализации логики убедитесь, что программная выборка совпадает с тем, что видит пользователь. Простейший способ — сравнить количество строк: выведите Выборка.Количество() или размер массива НайтиСтроки() в сообщение и сверьте с ожидаемым числом по списку.

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

Контрольные точки

Как проверить результат

Количество обработанных строк
Норма

Совпадает с числом строк, которые попадают под отбор в списке

Тревожный признак

Меньше — потерян отбор или взята только видимая порция; больше — проигнорированы условия

Поведение при пустом результате
Норма

Выводится понятное сообщение, ошибок нет

Тревожный признак

Исключение или молчаливая обработка «всего подряд»

Отбор по периоду
Норма

Документы за последний день периода включены

Тревожный признак

Документы последнего дня пропадают — значит, конец периода задан как 00:00:00

Итог

Что важно запомнить

Отобранные строки динамического списка не перебирают на клиенте — читают настройки отбора компоновщика и повторяют выборку запросом или СКД на сервере. Для табличных частей используют НайтиСтроки() со структурой. Выделенные строки и отбор — разные механизмы. Перед массовой обработкой проверяйте выборку в режиме просмотра и работайте порциями.

Частые вопросы

Можно ли получить строки динамического списка без повторного запроса к базе?

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

Почему НайтиСтроки() не находит строку, хотя значение в ней есть?

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

Как учесть отбор, который пользователь задал через строку поиска?

Условия стандартного поиска попадают в отбор динамического списка или настройки компоновщика в зависимости от конфигурации формы. Надёжнее всего брать итоговые настройки через Список.КомпоновщикНастроек.ПолучитьНастройки() и выполнять выборку через СКД — тогда будут учтены все действующие условия.

Чем отличается Выборка от Выгрузки результата запроса?

Выборка (метод Выбрать()) читает результат последовательно и экономит память — подходит для обработки больших объёмов. Выгрузить() возвращает таблицу значений целиком в памяти — удобно, когда нужен произвольный доступ, сортировка или повторный обход, но при больших объёмах расходует много памяти.

Как обработать отобранные строки, если их очень много?

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