В процессе разработки на платформе 1С:Предприятие 8 часто возникает задача программного перебора элементов справочника. Однако стандартный подход через метод Выбрать() часто заводит новичков в тупик, когда требуется отфильтровать данные по системным полям, таким как ПометкаУдаления. Ситуация усложняется, если имя справочника становится известным только в момент выполнения кода (например, когда пользователь выбирает объект из списка метаданных).
В этой статье мы подробно разберем, почему стандартный метод отбора может выдавать ошибку, и проанализируем наиболее эффективные способы решения этой задачи с использованием динамических запросов и механизмов платформы.
Рассмотрим ситуацию, с которой столкнулся автор темы. При попытке использовать следующий код:
Отбор = Новый Структура("ПометкаУдаления");
Отбор.ПометкаУдаления = Ложь;
Выборка = Справочники[ИмяСправочника].Выбрать(,, Отбор);
Платформа генерирует ошибку "Недопустимое значение параметра (параметр номер '3')". Выясним причину, обратившись к синтакс-помощнику. Метод Выбрать() объекта СправочникМенеджер имеет жесткие ограничения на использование структуры отбора:
Код, Наименование и те реквизиты, для которых в Конфигураторе установлен признак Индексировать.ПометкаУдаления является стандартным реквизитом, и оно по умолчанию не индексируется для целей метода Выбрать().Таким образом, использование Выбрать() для фильтрации «живых» записей невозможно. Рассмотрим правильные альтернативы.
Запросы в 1С — это самый гибкий и производительный инструмент для получения данных. Если имя справочника хранится в переменной, мы можем сформировать текст запроса динамически. Проанализируем пример кода, который позволяет обойти ограничение:
// Предположим, переменная ИмяСправочника содержит "Контрагенты"
ТекстЗапроса =
"ВЫБРАТЬ
| Таблица.Ссылка КАК Ссылка
|ИЗ
| Справочник.#ИмяСправочника КАК Таблица
|ГДЕ
| НЕ Таблица.ПометкаУдаления";
// Заменяем маркер на реальное имя таблицы
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяСправочника", ИмяСправочника);
Запрос = Новый Запрос(ТекстЗапроса);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Обработка объекта
Сообщить(Выборка.Ссылка);
КонецЦикла;
Важный нюанс: Мы использовали СтрЗаменить() для подстановки имени справочника. Это необходимо, так как в языке запросов 1С нельзя передать имя таблицы (метаданных) через параметры запроса (через &Параметр). Параметры работают только для значений (дат, ссылок, чисел).
Если в вашей конфигурации часто требуется выбирать данные из разных справочников по условию отсутствия пометки удаления, целесообразно оформить это в виде универсальной функции. Рассмотрим более продвинутый вариант с использованием функции СтрШаблон(), которая делает код более читаемым:
Функция ПолучитьВыборкуНеудаленных(ИмяСправочника) Экспорт
ШаблонЗапроса =
"ВЫБРАТЬ
| Т.Ссылка КАК Ссылка
|ИЗ
| Справочник.%1 КАК Т
|ГДЕ
| Т.ПометкаУдаления = ЛОЖЬ";
ТекстЗапроса = СтрШаблон(ШаблонЗапроса, ИмяСправочника);
Запрос = Новый Запрос(ТекстЗапроса);
Возврат Запрос.Выполнить().Выбрать();
КонецФункции
Такой подход позволяет минимизировать дублирование кода и легко поддерживать систему.
Иногда программисты выбирают путь «наименьшего сопротивления», просто перебирая все элементы и проверяя условие внутри цикла. Посмотрим на этот пример:
Выборка = Справочники[ИмяСправочника].Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.ПометкаУдаления Тогда
Продолжить;
КонецЕсли;
// Работаем только с полезными данными
КонецЦикла;
Внимание: Этот метод крайне не рекомендуется использовать для больших справочников (например, «Номенклатура» или «Контрагенты» с тысячами записей). Причина в том, что 1С сначала выгрузит из базы данных все записи, включая помеченные на удаление, и только потом начнет их отсеивать на стороне сервера приложений. Это создает лишнюю нагрузку на сеть и память.
Проанализируем несколько важных аспектов, которые помогут сделать вашу выборку еще лучше:
УстановитьПривилегированныйРежим(Истина).РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка").Метаданные.Справочники.Найти(ИмяСправочника).ПометкаУдаления в базе данных SQL без индекса может привести к полному сканированию таблицы (Full Scan). Если справочник содержит миллионы записей и такие запросы выполняются часто, рассмотрите возможность добавления индекса для этого поля в конфигураторе (хотя для стандартных реквизитов это не всегда доступно).Таким образом, мы выяснили, что для получения корректной выборки без помеченных на удаление объектов, единственным правильным и масштабируемым способом является использование динамически сформированного запроса. Это обеспечивает высокую скорость работы и гибкость при работе с любыми типами метаданных.