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