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