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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке на платформе 1С:Предприятие часто возникает задача поиска объекта (справочника или документа) по набору характеристик, которые хранятся в табличной части в виде пар «Имя — Значение». Такая структура данных называется моделью EAV (Entity-Attribute-Value). Основная сложность заключается в том, что в поисковом запросе (или временной таблице) нужные нам параметры часто представлены в виде отдельных колонок, а в базе данных они лежат «в столбик» — как разные строки одной табличной части.

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

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

Представим, что у нас есть справочник ХарактеристикиНоменклатуры. В нем есть табличная часть ДополнительныеРеквизиты с колонками Свойство (имя реквизита) и Значение. Нам необходимо найти конкретный элемент, у которого одновременно присутствуют, например, два свойства: «Цвет» со значением «Красный» и «Размер» со значением «44».

Если мы просто напишем условие ГДЕ (Свойство = "Цвет" И Значение = "Красный") И (Свойство = "Размер" И Значение = "44"), запрос вернет пустой результат. Это происходит потому, что одна строка табличной части не может одновременно содержать два разных имени свойства. Нам нужно искать комбинацию строк, относящихся к одной ссылке.

Решение 1: Динамическое построение текста запроса

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

Разберем этот процесс по шагам. Сначала мы подготавливаем временную таблицу с нашими искомыми значениями, а затем динамически достраиваем секции ВЫБРАТЬ и ПО.


// Пример динамического формирования запроса для поиска по набору свойств
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ИмяТаблицы", ИмяТаблицыРегистра);

// Базовый шаблон запроса
Запрос.Текст = 
"ВЫБРАТЬ
|    НЕОПРЕДЕЛЕНО КАК OBJID
|ПОМЕСТИТЬ ВТ_ТаблицаДанных
|;
|
|ВЫБРАТЬ
|    СоответствиеОбъектов.Ссылка,
|    СоответствиеОбъектов.СобственнаяСсылка КАК ВозвращаемаяСсылка,
|    НЕОПРЕДЕЛЕНО КАК Параметр
|ПОМЕСТИТЬ ВТПараметрыВыбора
|ИЗ
|    Справочник.СоответствиеОбъектов КАК СоответствиеОбъектов
|    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.СоответствиеОбъектов.ПараметрыВыбора КАК СоответствиеОбъектовПараметрыВыбора
|    ПО СоответствиеОбъектов.Ссылка = СоответствиеОбъектовПараметрыВыбора.Ссылка
|ГДЕ
|    СоответствиеОбъектов.ИмяТаблицы = &ИмяТаблицы
|    И НЕ СоответствиеОбъектов.ПометкаУдаления
|СГРУППИРОВАТЬ ПО
|    СоответствиеОбъектов.Ссылка,
|    СоответствиеОбъектов.СобственнаяСсылка
|;
|
|ВЫБРАТЬ
|    ВТПараметрыВыбора.ВозвращаемаяСсылка
|ИЗ
|    ВТПараметрыВыбора КАК ВТПараметрыВыбора
|    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ТаблицаДанных КАК ВТ_ТаблицаДанных
|    ПО ВТПараметрыВыбора.Параметр = ВТ_ТаблицаДанных.OBJID";

НомерПараметра = 0;
ПоляТаблицы = "";
СоединениеТаблиц = "";
ПараметрыЭлементаСправочника = "";

// КлючСсылки — это структура или соответствие с искомыми параметрами
Для каждого ЭлементКлюча Из КлючСсылки Цикл
    НомерПараметра = НомерПараметра + 1;
    
    // Формируем поля для временной таблицы значений
    ПоляТаблицы = ПоляТаблицы + "&ПараметрТаблицы" + НомерПараметра + " КАК " + ЭлементКлюча.Ключ + ",";
    
    // Формируем выборку значений из строк ТЧ в колонки через МАКСИМУМ(ВЫБОР...)
    ПараметрыЭлементаСправочника = ПараметрыЭлементаСправочника + "
    |    МАКСИМУМ(ВЫБОР
    |        КОГДА СоответствиеОбъектовПараметрыВыбора.ИмяРеквизита = &ИмяРеквизита" + НомерПараметра + "
    |            ТОГДА СоответствиеОбъектовПараметрыВыбора.ЗначениеРеквизита
    |        ИНАЧЕ NULL
    |    КОНЕЦ) КАК Параметр" + НомерПараметра + ",";
    
    // Формируем условие соединения
    СоединениеТаблиц = СоединениеТаблиц + "ВТПараметрыВыбора.Параметр" + НомерПараметра + " = ВТ_ТаблицаДанных." + ЭлементКлюча.Ключ + "
    | И ";
    
    Запрос.УстановитьПараметр("ПараметрТаблицы" + НомерПараметра, ЭлементКлюча.Значение);
    Запрос.УстановитьПараметр("ИмяРеквизита" + НомерПараметра, ЭлементКлюча.Ключ);
КонецЦикла;

// Убираем лишние символы в конце строк
ПоляТаблицы = Лев(ПоляТаблицы, СтрДлина(ПоляТаблицы) - 1);
ПараметрыЭлементаСправочника = Лев(ПараметрыЭлементаСправочника, СтрДлина(ПараметрыЭлементаСправочника) - 1);
СоединениеТаблиц = Лев(СоединениеТаблиц, СтрДлина(СоединениеТаблиц) - 3);

// Финальная замена в тексте запроса
Запрос.Текст = СтрЗаменить(Запрос.Текст, "НЕОПРЕДЕЛЕНО КАК OBJID", ПоляТаблицы);
Запрос.Текст = СтрЗаменить(Запрос.Текст, "НЕОПРЕДЕЛЕНО КАК Параметр", ПараметрыЭлементаСправочника);
Запрос.Текст = СтрЗаменить(Запрос.Текст, "ВТПараметрыВыбора.Параметр = ВТ_ТаблицаДанных.OBJID", СоединениеТаблиц);

Проанализируем этот код. Основная идея здесь — использование функции МАКСИМУМ(ВЫБОР...) при группировке. Это позволяет нам «развернуть» строки табличной части в колонки временной таблицы ВТПараметрыВыбора. Каждая колонка будет содержать значение конкретного реквизита для данной ссылки. Затем мы просто соединяем полученную «плоскую» таблицу с нашей таблицей условий.

Решение 2: Метод подсчета совпадений (статический запрос)

Если нам не хочется заниматься сложной сборкой строк, мы можем использовать более элегантный способ, который часто работает быстрее. Разобьем его на этапы:

  1. Поместим искомые пары «Свойство-Значение» во временную таблицу ВТ_Критерии.
  2. Соединим эту таблицу с табличной частью справочника по обоим полям.
  3. Сгруппируем результат по Ссылка.
  4. В секции ИМЕЮЩИЕ укажем, что количество совпадений должно быть равно количеству искомых параметров.

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


// ВТ_Критерии содержит две колонки: Свойство и Значение
// &КоличествоКритериев — число строк в ВТ_Критерии

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

Важный момент: этот способ предполагает, что в табличной части не может быть дублей одного и того же свойства для одной ссылки. Если дубли возможны, используйте КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ТЧ.Свойство).

Решение 3: Использование объекта СхемаЗапроса

Если вы предпочитаете программное управление структурой запроса без манипуляций со строками (СтрЗаменить), рассмотрите использование СхемаЗапроса. Это мощный инструмент платформы, позволяющий объектно описывать источники данных, условия и поля.

Проанализируем преимущества СхемаЗапроса:

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

Работа с EAV-структурами (дополнительными реквизитами) может быть медленной на больших объемах данных. Чтобы ваш поиск летал, обратите внимание на следующие советы:

1. Индексация: Проверьте в конфигураторе свойства вашей табличной части. Поля Свойство и Значение (или их аналоги) обязательно должны иметь признак «Индексировать». Без этого СУБД будет выполнять полное сканирование таблицы (Full Scan) при каждом поиске, что критично для производительности.

2. Типизация значений: Если колонка Значение имеет составной тип (например, может хранить и Число, и Ссылку на другой справочник), поиск будет работать медленнее, чем по строго типизированному полю. По возможности ограничивайте набор типов.

3. Использование БСП: Если вы работаете в современной конфигурации на базе «Библиотеки стандартных подсистем» (БСП), не изобретайте велосипед. Проанализируйте возможности общего модуля УправлениеСвойствами. В БСП часто реализованы механизмы кэширования и быстрого поиска по дополнительным сведениям через специальные регистры.

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

Мы рассмотрели основные подходы к поиску объектов по набору свойств в 1С. Динамическое построение запроса через МАКСИМУМ(ВЫБОР...) удобно, когда нужно вывести значения свойств в колонки, а метод подсчета совпадений идеален для быстрого фильтра «нашел/не нашел». Выбирайте тот метод, который лучше отвечает требованиям вашей задачи!

← На главную