Как реализовать поиск номенклатуры по нескольким дополнительным реквизитам одновременно в 1С

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

При работе с типовыми конфигурациями, такими как УТ 11, ERP или КА, разработчики часто сталкиваются с необходимостью поиска объектов по набору дополнительных реквизитов. Сложность заключается в том, что в системе 1С дополнительные реквизиты хранятся в табличной части (например, ДополнительныеРеквизиты), где каждая пара «Свойство — Значение» — это отдельная строка. Простой запрос с условием ГДЕ или оператором В находит объекты, у которых совпадает хотя бы одно свойство (логическое ИЛИ), в то время как нам обычно требуется полное совпадение всех заданных свойств (логическое И). Это классическая задача реляционного деления.

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

Проблема: Почему простой запрос не находит нужные данные?

Проанализируем ситуацию, описанную автором темы. Попытка использовать конструкцию (Реквизиты.Свойство, Реквизиты.Значение) В (ВЫБРАТЬ...) приведет к тому, что система вернет номенклатуру, если у нее есть хотя бы одна строка, соответствующая любой паре из фильтра. Если мы ищем номенклатуру с 7 свойствами, а у товара совпало только 1 из них, он все равно попадет в выборку. Нам же нужно убедиться, что в табличной части присутствуют все семь нужных строк.

Рассмотрим по шагам три основных метода решения этой задачи.

Метод 1. Группировка и условие ИМЕЮЩИЕ (Реляционное деление)

Этот метод считается наиболее эффективным и масштабируемым. Вместо того чтобы пытаться найти всё в одну строку, мы отбираем все подходящие строки и проверяем, сколько их набралось для каждой конкретной номенклатуры.

Проанализируем алгоритм работы этого метода:

  1. Помещаем искомые пары «Свойство — Значение» во временную таблицу.
  2. Выбираем из табличной части ДополнительныеРеквизиты только те записи, которые есть в нашей временной таблице.
  3. Группируем результат по полю Ссылка (номенклатура).
  4. Устанавливаем условие ИМЕЮЩИЕ КОЛИЧЕСТВО(Свойство) = &КоличествоИскомыхРеквизитов.

Посмотрим на пример реализации такого запроса:


// Предположим, ТЗ_Фильтр содержит колонки Свойство и Значение
// КоличествоРеквизитов = ТЗ_Фильтр.Количество();

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

Запрос.Текст = 
"ВЫБРАТЬ
|	Фильтр.Свойство КАК Свойство,
|	Фильтр.Значение КАК Значение
|ПОМЕСТИТЬ ВТ_Фильтр
|ИЗ
|	&ТЗ_Фильтр КАК Фильтр
|;
|
|ВЫБРАТЬ
|	ТчРеквизиты.Ссылка КАК Номенклатура
|ИЗ
|	Справочник.Номенклатура.ДополнительныеРеквизиты КАК ТчРеквизиты
|ГДЕ
|	ТчРеквизиты.Ссылка.Артикул = &Артикул
|	И (ТчРеквизиты.Свойство, ТчРеквизиты.Значение) В (ВЫБРАТЬ Свойство, Значение ИЗ ВТ_Фильтр)
|
|СГРУППИРОВАТЬ ПО
|	ТчРеквизиты.Ссылка
|
|ИМЕЮЩИЕ
|	КОЛИЧЕСТВО(ТчРеквизиты.Свойство) = &Количество";

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

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

Метод 2. Последовательные внутренние соединения

Если количество реквизитов фиксировано или невелико, можно использовать несколько соединений одной и той же таблицы самой с собой. Каждое новое соединение «накладывает» дополнительный фильтр И.

Рассмотрим, как это выглядит в коде:


ВЫБРАТЬ
	Номенклатура.Ссылка
ИЗ
	Справочник.Номенклатура КАК Номенклатура
	ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК Свойство1
		ПО Номенклатура.Ссылка = Свойство1.Ссылка 
		И Свойство1.Свойство = &Свойство1 И Свойство1.Значение = &Значение1
	ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК Свойство2
		ПО Номенклатура.Ссылка = Свойство2.Ссылка 
		И Свойство2.Свойство = &Свойство2 И Свойство2.Значение = &Значение2
ГДЕ
	Номенклатура.Артикул = &Артикул

Этот метод интуитивно понятен, но имеет существенный недостаток: если вам нужно искать по 7 реквизитам, вам придется сделать 7 соединений. Это создает большую нагрузку на планировщик запросов СУБД и может существенно замедлить выполнение, если справочник Номенклатура содержит сотни тысяч записей.

Метод 3. Программное формирование запроса (через ВЫБОР)

Как мы видели в обсуждении, автор пытался использовать конструкцию ВЫБОР КОГДА... ТОГДА ИСТИНА. Однако сама по себе она не решает проблему "И", так как выполняется для каждой строки отдельно. Чтобы этот метод заработал, его нужно комбинировать с суммированием баллов.

Проанализируем пример логики:

  1. Для каждой строки табличной части проверяем, совпадает ли она с одним из наших условий.
  2. Если совпадает — присваиваем 1, если нет — 0.
  3. Суммируем эти значения по каждой номенклатуре.
  4. Если сумма равна количеству искомых свойств — объект найден.

ВЫБРАТЬ
	Тч.Ссылка,
	СУММА(ВЫБОР 
		КОГДА Тч.Свойство = &Свойство1 И Тч.Значение = &Значение1 ТОГДА 1
		КОГДА Тч.Свойство = &Свойство2 И Тч.Значение = &Значение2 ТОГДА 1
		ИНАЧЕ 0 
	КОНЕЦ) КАК КоличествоСовпадений
ПОМЕСТИТЬ ВТ_Подсчет
ИЗ
	Справочник.Номенклатура.ДополнительныеРеквизиты КАК Тч
ГДЕ
	Тч.Ссылка.Артикул = &Артикул
СГРУППИРОВАТЬ ПО
	Тч.Ссылка;

ВЫБРАТЬ Ссылка ИЗ ВТ_Подсчет ГДЕ КоличествоСовпадений = &НужноеКоличество

Почему могут возникать ошибки при сравнении значений?

В Сообщении 16 автор упоминает, что внутреннее соединение не отрабатывает, когда у ЗначенияСвойства есть владелец. Разберем причину. В 1С дополнительные реквизиты используют тип Характеристика.ДополнительныеРеквизитыИСведения. Это составной тип данных. При сравнении значений в запросе через временные таблицы (ВТ) крайне важно соблюдать следующие правила:

  1. Типизация ВТ: При создании временной таблицы из ТаблицыЗначений, колонки должны быть жестко типизированы. Если в колонке Значение будет просто ЛюбаяСсылка, индекс в базе данных может не сработать. Используйте ОписаниеТипов при создании ТаблицыЗначений.
  2. Использование ВЫРАЗИТЬ: Если вы знаете, что значение реквизита — это ссылка на справочник ЗначенияСвойствОбъектов, используйте в запросе ВЫРАЗИТЬ(Тч.Значение КАК Справочник.ЗначенияСвойствОбъектов). Это поможет оптимизатору SQL правильно построить план запроса.
  3. Пустые значения: Если один из реквизитов в фильтре не заполнен, но должен быть проверен, логика внутреннего соединения или оператора В может отсечь такие записи. Всегда проверяйте, что в фильтр попадают только реально заполненные свойства.

Рекомендации по оптимизации

Для ускорения поиска в крупных базах данных УТ 11 рекомендуется:

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

← На главную