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