Как правильно составить запрос к характеристикам с несколькими дополнительными реквизитами?

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

При работе с механизмами 1С:Предприятия, такими как дополнительные реквизиты и сведения (архитектура Entity-Attribute-Value или EAV), разработчики часто сталкиваются с задачей поиска объекта, у которого одновременно задано несколько конкретных значений свойств. Например, нужно найти характеристику номенклатуры, у которой «Длина» равна 1, «Ширина» равна 2, а «Цвет» — «Серый».

Основная сложность заключается в том, что в базе данных эти свойства хранятся в виде строк табличной части или записей регистра сведений. Это значит, что для одной характеристики существует несколько записей в таблице реквизитов. Прямое условие ГДЕ Свойство = &Свойство1 И Свойство = &Свойство2 никогда не сработает, так как в одной строке не могут находиться два разных значения свойства одновременно.

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

Способ 1: Использование внутренних соединений (Inner Join)

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


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

Плюсы метода:

  1. Высокая читаемость: сразу понятно, какие фильтры накладываются.
  2. Возможность легко выводить значения этих свойств в отдельные колонки результата.

Минусы метода:

  1. Падение производительности: Каждое соединение заставляет СУБД строить сложные планы выполнения. Если свойств много (например, 10–15), запрос может начать «тормозить» даже на средних объемах данных.
  2. Сложность динамического построения: если количество фильтров меняется, нужно программно генерировать текст запроса с разным количеством JOIN.

Способ 2: Группировка и условие на количество (HAVING COUNT)

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

Рассмотрим реализацию этого алгоритма:


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

Разберем по шагам, как это работает:

  1. В блоке ГДЕ мы указываем все пары «Свойство-Значение», используя оператор ИЛИ. Это позволяет СУБД эффективно использовать индексы по свойствам.
  2. Мы группируем результат по полю Ссылка (владельцу реквизитов).
  3. Условие ИМЕЮЩИЕ КОЛИЧЕСТВО(...) = N гарантирует, что в результат попадут только те характеристики, для которых нашлось ровно N совпадений. Если для объекта нашлось только 2 свойства из 3 запрошенных, он будет отсеян.

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

Способ 3: Использование временных таблиц и оператора «В»

Иногда требуется поэтапная фильтрация, особенно если данных очень много и нужно максимально упростить работу оптимизатору СУБД. Мы можем сначала отобрать ссылки, подходящие по первому свойству, поместить их во временную таблицу, а затем отобрать из них те, что подходят по второму, и так далее.

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


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

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

Этот метод кажется громоздким, но в некоторых случаях на PostgreSQL или очень больших объемах MS SQL Server он показывает стабильное время выполнения, так как СУБД работает с уже сокращенными наборами данных на каждом шаге.

Сравнение производительности и рекомендации

Проанализируем ситуацию с точки зрения ресурсов системы. Как показывают практические замеры (в том числе приведенные в обсуждении темы), второй способ (с группировкой и HAVING) зачастую оказывается в несколько раз быстрее классических соединений. Это связано с тем, что СУБД выполняет одно сканирование индекса вместо построения множества временных структур для сопоставления строк.

Почему соединения могут работать медленно?

Когда мы используем JOIN, СУБД вынуждена сопоставлять каждую найденную строку с каждой. Если по первому фильтру найдено 1000 объектов и по второму 1000, системе нужно проверить множество комбинаций. Группировка же работает линейно по одному набору данных.

Тонкие моменты при работе с доп. реквизитами

Выясним причины, по которым запрос может работать медленно даже при правильной структуре:

Рассмотрим ситуацию, когда свойств нет в объекте. Если вам нужно найти объекты, у которых нет определенных свойств, лучше всего использовать левое соединение с проверкой на ЕСТЬ NULL.

Подведем итог

Для большинства задач поиска в 1С оптимальным является следующий алгоритм:

  1. Используйте единый запрос к таблице дополнительных реквизитов.
  2. Применяйте условие ИЛИ для всех искомых пар «Свойство-Значение».
  3. Применяйте СГРУППИРОВАТЬ ПО объекту-владельцу.
  4. Используйте условие ИМЕЮЩИЕ КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Свойство) = &N, где N — количество фильтров.

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

← На главную