Как отобрать номенклатуру по точному соответствию нескольким свойствам и значениям в запросе 1С

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

При разработке подборов или сложных фильтров в 1С:Предприятии часто возникает задача: найти объекты (например, номенклатуру), которые обладают строго определенным набором характеристик. Обычный отбор через условие В (&Массив) здесь не подходит, так как он работает по логике "ИЛИ" (найдет объект, если у него есть хотя бы одно свойство из списка) и не исключает объекты, у которых есть "лишние" свойства. В этой статье мы подробно разберем, как реализовать поиск по точному вхождению.

Постановка задачи и анализ проблемы

Представим ситуацию: у нас есть таблица с требованиями (например, Свойство "Цвет" = "Красный" и Свойство "Материал" = "Сталь"). Нам нужно найти только ту номенклатуру, у которой установлены именно эти два значения. Если у товара есть еще и третье свойство (например, "Размер"), он не должен попасть в выборку, так как это не является полным совпадением условий пользователя.

Проанализируем типичную ошибку начинающих разработчиков. Использование условия ГДЕ Свойство В (&МассиыСвойств) И Значение В (&МассивЗначений) в запросе к регистру сведений ЗначенияСвойствОбъектов не связывает конкретное свойство с его значением. Более того, запрос вернет несколько строк для одной номенклатуры, и мы получим список всех товаров, имеющих хотя бы одну подходящую пару.

Метод 1: Сравнение по количеству (Реляционное деление)

Это наиболее эффективный и лаконичный способ решения задачи в 1С. Суть метода заключается в том, чтобы сгруппировать записи регистра по объекту и проверить три условия:

  1. Количество свойств объекта, совпавших с эталонными, равно количеству свойств в поиске.
  2. Общее количество свойств у этого объекта в базе также равно количеству свойств в поиске.

Если оба условия выполняются, значит, набор свойств идентичен.

Рассмотрим по шагам реализацию этого алгоритма. Сначала подготовим данные на стороне встроенного языка:


// Предположим, что данные получены из ТабличногоПоля на форме
ТЗ_Отбор = Новый ТаблицаЗначений;
ТЗ_Отбор.Колонки.Добавить("Свойство", Новый ОписаниеТипов("ПланВидовХарактеристикСсылка.СвойстваОбъектов"));
ТЗ_Отбор.Колонки.Добавить("Значение", Новый ОписаниеТипов("СправочникСсылка.ЗначенияСвойствОбъектов"));

Для Каждого СтрокаТП Из ТабличноеПоле1 Цикл
    НоваяСтр = ТЗ_Отбор.Добавить();
    НоваяСтр.Свойство = СтрокаТП.КолонкаСвойство;
    НоваяСтр.Значение = СтрокаТП.КолонкаЗначение;
КонецЦикла;

// Важно: убираем дубли, если пользователь ввел одну и ту же пару дважды
ТЗ_Отбор.Свернуть("Свойство, Значение");
КоличествоТребуемыхСвойств = ТЗ_Отбор.Количество();

Теперь подготовим запрос. Мы будем использовать временную таблицу для передачи условий и связку через В (ВЫБРАТЬ...) по кортежу полей.


Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТЗ_Отбор", ТЗ_Отбор);
Запрос.УстановитьПараметр("ВсегоСвойств", КоличествоТребуемыхСвойств);

Запрос.Текст = 
"ВЫБРАТЬ
|	ТЗ.Свойство КАК Свойство,
|	ТЗ.Значение КАК Значение
|ПОМЕСТИТЬ ВТ_Условия
|ИЗ
|	&ТЗ_Отбор КАК ТЗ
|ИНДЕКСИРОВАТЬ ПО
|	Свойство,
|	Значение
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	Регистр.Объект КАК Номенклатура
|ИЗ
|	РегистрСведений.ЗначенияСвойствОбъектов КАК Регистр
|ГДЕ
|	Регистр.Объект ССЫЛКА Справочник.Номенклатура
|СГРУППИРОВАТЬ ПО
|	Регистр.Объект
|ИМЕЮЩИЕ
|	СУММА(ВЫБОР
|			КОГДА (Регистр.Свойство, Регистр.Значение) В
|				(ВЫБРАТЬ
|					Условия.Свойство,
|					Условия.Значение
|				ИЗ
|					ВТ_Условия КАК Условия)
|				ТОГДА 1
|			ИНАЧЕ 0
|		КОНЕЦ) = &ВсегоСвойств
|	И КОЛИЧЕСТВО(Регистр.Свойство) = &ВсегоСвойств";

Проанализируем, как работает этот запрос. Блок СУММА(ВЫБОР...) подсчитывает, сколько свойств объекта "попали" в наш эталонный список. Если это число равно количеству строк в нашей таблице отбора, значит, все нужные свойства у объекта есть. Второе условие КОЛИЧЕСТВО(Регистр.Свойство) = &ВсегоСвойств отсекает те объекты, у которых помимо нужных свойств есть какие-либо другие.

Метод 2: Использование двойного отрицания

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

Разберем логику этого подхода:

  1. Найдем объекты, у которых значение хотя бы одного свойства из нашего списка не совпадает с требуемым.
  2. Найдем объекты, у которых есть свойства, вообще не входящие в наш список.
  3. Вычтем эти два множества из общего списка объектов, имеющих свойства.

Посмотрим на пример реализации через временные таблицы:


// 1. Получаем список объектов, у которых ЕСТЬ ЛИШНИЕ свойства
// или свойства с НЕПРАВИЛЬНЫМИ значениями
Запрос.Текст = 
"ВЫБРАТЬ РАЗЛИЧНЫЕ
|	Регистр.Объект
|ПОМЕСТИТЬ ВТ_Исключить
|ИЗ
|	РегистрСведений.ЗначенияСвойствОбъектов КАК Регистр
|ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Условия КАК Условия
|	ПО Регистр.Свойство = Условия.Свойство
|	И Регистр.Значение = Условия.Значение
|ГДЕ
|	Условия.Свойство ЕСТЬ NULL"; // Значит, в базе есть свойство, которого нет в фильтре

После этого основным запросом мы выбираем объекты, которые имеют нужные свойства и не входят в таблицу ВТ_Исключить. Этот метод иногда работает быстрее, так как индексы по NULL значениям при левом соединении эффективно обрабатываются планировщиком задач SQL.

Важные нюансы и оптимизация

При работе со свойствами важно учитывать специфику хранения данных в 1С:

  1. Индексация: Обязательно индексируйте временную таблицу условий по полям Свойство и Значение. Без этого при большом количестве записей в регистре (сотни тысяч и более) запрос будет выполняться крайне медленно из-за полного сканирования таблиц (Table Scan).
  2. Типы данных: Поле Значение в регистре ЗначенияСвойствОбъектов обычно имеет составной тип. При формировании временной таблицы ВТ_Условия убедитесь, что типы значений в колонке ТЗ точно соответствуют типам в базе, иначе соединение может не сработать.
  3. Пустые значения: Выясним причину, если запрос ничего не возвращает при наличии визуально схожих данных. Возможно, в базе создана запись с пустым значением свойства, а в фильтре вы это не учитываете. Если "отсутствие записи" и "пустое значение" для вашей логики — это одно и то же, запрос потребует усложнения через ЕСТЬNULL.
  4. Современные конфигурации (БСП): Если вы работаете в современной базе (УТ 11, ERP 2, БП 3), помните, что дополнительные сведения хранятся в регистре ДополнительныеСведения, а дополнительные реквизиты — в табличных частях самих объектов. Логика запроса при этом остается прежней, меняются только имена таблиц.

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

← На главную