Как найти объекты, которые содержат все элементы из заданного набора в одном запросе 1С

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

В практике программирования на платформе 1С часто возникает задача поиска объектов, которые обладают не просто одним из свойств, а полным набором заданных характеристик. В реляционной алгебре такая операция называется «реляционным делением». Типичный пример: поиск картинок, на которых одновременно присутствуют все выбранные пользователем цвета (например, красный, синий и зеленый одновременно).

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

Способ 1. Группировка и подсчет количества (HAVING COUNT)

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

Рассмотрим реализацию пошагово:

  1. Поместим искомые цвета во временную таблицу или передадим их массивом в параметры запроса.
  2. Сделаем выборку из регистра, ограничив её только нужными цветами через условие ГДЕ.
  3. Сгруппируем результат по полю Картинка.
  4. В секции ИМЕЮЩИЕ установим условие на количество.

Пример кода запроса:


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

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

Способ 2. Подсчет количества через вложенный запрос

Если количество искомых элементов заранее неизвестно и вычисляется динамически в этом же запросе, мы можем использовать вложенный запрос прямо в условии ИМЕЮЩИЕ. Это позволит избежать предварительного подсчета количества в коде на встроенном языке.

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


ВЫБРАТЬ
    Состав.Картинка КАК Картинка
ИЗ
    РегистрСведений.СоставКартинок КАК Состав
ГДЕ
    Состав.Цвет В (ВЫБРАТЬ ВТ_ИскомыеЦвета.Цвет ИЗ ВТ_ИскомыеЦвета)
СГРУППИРОВАТЬ ПО
    Состав.Картинка
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Состав.Цвет) = (ВЫБРАТЬ КОЛИЧЕСТВО(*) ИЗ ВТ_ИскомыеЦвета)

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

Способ 3. Метод двойного отрицания (NOT EXISTS)

Этот метод более сложен для понимания, но часто работает быстрее на больших объемах данных, так как позволяет СУБД прекратить проверку объекта, как только обнаружено отсутствие хотя бы одного свойства. Логика звучит так: «Найти такие картинки, для которых не существует такого цвета в искомом списке, который не был бы привязан к этой картинке».

В терминах 1С это можно реализовать через левое соединение и проверку на NULL:

  1. Формируем декартово произведение (все картинки × все искомые цвета).
  2. Левым соединением присоединяем реальную таблицу связей.
  3. Ищем те картинки, где после соединения образовался NULL (значит, связи нет).
  4. Исключаем такие картинки из общего списка.

Способ 4. Цепочка внутренних соединений (INNER JOIN)

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

Рассмотрим пример для двух цветов:


ВЫБРАТЬ
    Т1.Картинка
ИЗ
    РегистрСведений.Цвета КАК Т1
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.Цвета КАК Т2
    ПО Т1.Картинка = Т2.Картинка
ГДЕ
    Т1.Цвет = &Цвет1
    И Т2.Цвет = &Цвет2

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

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

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

Выбор конкретного метода зависит от ваших условий: Способ 1 является наиболее универсальным и рекомендуемым для большинства задач в 1С благодаря своей читаемости и простоте поддержки.

← На главную