Как отобрать элементы справочника по нескольким строкам табличной части в запросе 1С

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

При разработке на платформе 1С:Предприятие часто возникает задача поиска объектов, которые обладают сразу несколькими характеристиками. Типичный пример: поиск товара в справочнике Номенклатура или ХарактеристикиНоменклатуры, у которого в табличной части ДополнительныеРеквизиты заданы конкретные пары «Свойство — Значение». Трудность заключается в том, что каждое свойство хранится в отдельной строке, и обычное условие ГДЕ с оператором И не сработает, так как одна строка не может содержать разные значения одновременно.

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

Почему стандартный отбор через "И" не работает

Рассмотрим ситуацию, описанную в обсуждении. Программист пытается найти характеристику, у которой «Цвет» — «Красный» И «Размер» — «42». Если написать условие так:


ГДЕ (ДополнительныеРеквизиты.Свойство = &СвойствоЦвет И ДополнительныеРеквизиты.Значение = &ЗначениеКрасный)
  И (ДополнительныеРеквизиты.Свойство = &СвойствоРазмер И ДополнительныеРеквизиты.Значение = &Значение42)

Такой запрос всегда вернет пустой результат. Это происходит потому, что система анализирует каждую строку табличной части по отдельности. В базе данных нет строки, где поле Свойство одновременно равно и цвету, и размеру. Нам же нужно найти объект, у которого есть минимум две разные строки, соответствующие этим условиям.

Решение №1: Группировка и условие ИМЕЮЩИЕ (HAVING)

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

Разберем этот алгоритм пошагово:

  1. Отбираем строки табличной части, которые соответствуют хотя бы одной паре «Свойство-Значение».
  2. Группируем результат по полю Ссылка.
  3. В блоке ИМЕЮЩИЕ проверяем, что количество различных свойств в группе совпадает с количеством условий, которые мы передали.

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


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

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

Решение №2: Использование внутренних соединений (JOIN)

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

Выясним причину эффективности этого метода: каждое новое ВНУТРЕННЕЕ СОЕДИНЕНИЕ отсекает объекты, не соответствующие очередному критерию, еще на этапе выборки, не дожидаясь агрегации всей таблицы.


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

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

Решение №3: Метод временной таблицы условий

Если нам необходимо найти объект по длинному списку свойств (например, 10–20 параметров), загромождать запрос множеством соединений или условий ИЛИ неудобно. В таком случае рассмотрим профессиональный подход с использованием временной таблицы.

Разберем реализацию по этапам:

  1. Поместим искомые пары «Свойство-Значение» во временную таблицу (например, из переданной в запрос ТаблицыЗначений).
  2. Соединим основную таблицу ДополнительныеРеквизиты с этой временной таблицей по обоим полям (свойство и значение).
  3. Выполним группировку по ссылке и сравним количество строк, как в первом способе.

Пример реализации:


// Предположим, ТЗ_Условия уже передана в МенеджерВременныхТаблиц как ВТ_Условия
// В ней колонки: Свойство, Значение

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

Решение №4: Использование механизма "В (ВЫБРАТЬ...)"

Иногда удобно использовать вложенные запросы с оператором В. Этот метод читабелен и легко строится динамически.


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

Хотя визуально кажется, что это два подзапроса, современные СУБД (MS SQL, PostgreSQL) оптимизируют такие конструкции в «semi-joins», и по скорости они сопоставимы с внутренними соединениями.

Особенности работы с БСП (Библиотека стандартных подсистем)

Если ваша конфигурация построена на БСП (УТ 11, ERP, КА 2, БП 3), важно помнить несколько моментов:

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

Мы проанализировали ситуацию и выяснили, что для отбора по нескольким строкам одной таблицы нельзя использовать простое логическое «И». Наиболее универсальным и "гламурным" решением (как выразился автор темы) является использование группировки СГРУППИРОВАТЬ ПО совместно с условием ИМЕЮЩИЕ КОЛИЧЕСТВО. Это позволяет динамически изменять количество условий, не меняя структуру самого запроса, а лишь передавая разные параметры.

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

← На главную