В практике программирования на платформе 1С часто возникает задача поиска объектов, которые обладают не просто одним из свойств, а полным набором заданных характеристик. В реляционной алгебре такая операция называется «реляционным делением». Типичный пример: поиск картинок, на которых одновременно присутствуют все выбранные пользователем цвета (например, красный, синий и зеленый одновременно).
Разберем подробно несколько способов решения этой задачи, начиная от самых простых и заканчивая наиболее производительными для больших объемов данных.
Проанализируем самый лаконичный метод, предложенный в обсуждении. Его суть заключается в том, чтобы выбрать все записи, подходящие под фильтр «хотя бы один из списка», сгруппировать их по объекту и проверить, совпадает ли количество найденных уникальных свойств с количеством искомых свойств.
Рассмотрим реализацию пошагово:
ГДЕ.Картинка.ИМЕЮЩИЕ установим условие на количество.Пример кода запроса:
ВЫБРАТЬ
РегистрЦвета.Картинка КАК Картинка
ИЗ
РегистрСведений.ЦветаКартинок КАК РегистрЦвета
ГДЕ
РегистрЦвета.Цвет В (&МассивИскомыхЦветов)
СГРУППИРОВАТЬ ПО
РегистрЦвета.Картинка
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ РегистрЦвета.Цвет) = &КоличествоИскомыхЦветов
Важный нюанс: использование ключевого слова РАЗЛИЧНЫЕ внутри функции КОЛИЧЕСТВО критически важно. Если в данных по какой-то причине присутствуют дубли (например, две записи «Картинка1 — Красный»), то обычный подсчет выдаст неверный результат, и картинка попадет в выборку ошибочно.
Если количество искомых элементов заранее неизвестно и вычисляется динамически в этом же запросе, мы можем использовать вложенный запрос прямо в условии ИМЕЮЩИЕ. Это позволит избежать предварительного подсчета количества в коде на встроенном языке.
Посмотрим на пример такого решения:
ВЫБРАТЬ
Состав.Картинка КАК Картинка
ИЗ
РегистрСведений.СоставКартинок КАК Состав
ГДЕ
Состав.Цвет В (ВЫБРАТЬ ВТ_ИскомыеЦвета.Цвет ИЗ ВТ_ИскомыеЦвета)
СГРУППИРОВАТЬ ПО
Состав.Картинка
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Состав.Цвет) = (ВЫБРАТЬ КОЛИЧЕСТВО(*) ИЗ ВТ_ИскомыеЦвета)
Проанализируем ситуацию с производительностью: такой подход удобен, но на огромных таблицах СУБД может выполнять вложенный запрос для каждой группы. Чтобы этого избежать, рекомендуется сначала вычислить количество и передать его параметром или подготовить временную таблицу из одной строки с числом элементов.
Этот метод более сложен для понимания, но часто работает быстрее на больших объемах данных, так как позволяет СУБД прекратить проверку объекта, как только обнаружено отсутствие хотя бы одного свойства. Логика звучит так: «Найти такие картинки, для которых не существует такого цвета в искомом списке, который не был бы привязан к этой картинке».
В терминах 1С это можно реализовать через левое соединение и проверку на NULL:
NULL (значит, связи нет).Если мы точно знаем, что цветов всегда мало (например, ровно два или три), можно использовать последовательное соединение таблицы самой с собой. Выясним причину популярности этого метода: он эффективно использует индексы по измерениям регистра.
Рассмотрим пример для двух цветов:
ВЫБРАТЬ
Т1.Картинка
ИЗ
РегистрСведений.Цвета КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.Цвета КАК Т2
ПО Т1.Картинка = Т2.Картинка
ГДЕ
Т1.Цвет = &Цвет1
И Т2.Цвет = &Цвет2
Этот метод требует программного построения текста запроса, если количество цветов меняется, но для фиксированных фильтров он показывает отличную скорость.
Для того чтобы запрос работал максимально быстро, придерживайтесь следующих правил:
Картинка и Цвет проиндексированы. В 1С измерения индексируются автоматически, но порядок полей в индексе имеет значение.ВТ_ИскомыеЦвета) и создавайте по ней индекс по полю Цвет.ГДЕ Цвет В (...) перед группировкой. Это позволяет серверу БД отбросить миллионы лишних записей еще до начала этапа агрегации данных.Выбор конкретного метода зависит от ваших условий: Способ 1 является наиболее универсальным и рекомендуемым для большинства задач в 1С благодаря своей читаемости и простоте поддержки.