Как отфильтровать элементы по дополнительному реквизиту в запросе 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

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

Понимание архитектуры хранения дополнительных реквизитов

Прежде чем переходить к написанию кода, проанализируем ситуацию и выясним, где физически хранятся эти данные в базе. В механизме БСП для каждого справочника (например, Справочник.Партнеры), для которого включено использование дополнительных реквизитов, создается одноименная табличная часть — ДополнительныеРеквизиты.

Каждая строка этой табличной части содержит два ключевых поля:

  1. Свойство — ссылка на план видов характеристик ДополнительныеРеквизитыИСведения (указывает, какой именно реквизит мы рассматриваем).
  2. Значение — значение этого свойства для конкретного элемента справочника.

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

Решение 1. Базовый фильтр по одному дополнительному реквизиту

Допустим, нам нужно выбрать всех партнеров, у которых определенный дополнительный реквизит (например, "Заблокирован") имеет значение ЛОЖЬ. При этом важно учесть важный нюанс: у некоторых партнеров этот реквизит может быть вообще не заполнен (строка в табличной части отсутствует). С точки зрения логики бизнеса, если реквизит не задан, значит, партнер не заблокирован, и его тоже нужно включить в результат запроса.

Для решения этой задачи мы должны использовать конструкцию ЛЕВОЕ СОЕДИНЕНИЕ. Если использовать внутреннее соединение, мы потеряем всех партнеров, у которых нет ни одного дополнительного реквизита в табличной части.

Посмотрим на пример правильного построения запроса:


ВЫБРАТЬ
    Партнеры.Ссылка КАК Ссылка
ИЗ
    Справочник.Партнеры КАК Партнеры
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партнеры.ДополнительныеРеквизиты КАК ПартнерыДополнительныеРеквизиты
        ПО Партнеры.Ссылка = ПартнерыДополнительныеРеквизиты.Ссылка
        И ПартнерыДополнительныеРеквизиты.Свойство = &Свойство
ГДЕ
    ПартнерыДополнительныеРеквизиты.Значение = ЛОЖЬ
    ИЛИ ПартнерыДополнительныеРеквизиты.Значение ЕСТЬ NULL

Разберем подробнее ключевые моменты этого запроса:

  1. Мы соединяем справочник Партнеры с его собственной табличной частью ДополнительныеРеквизиты.
  2. Критически важно: ограничение по конкретному свойству (ПартнерыДополнительныеРеквизиты.Свойство = &Свойство) мы накладываем непосредственно в условии связи ПО, а не в секции ГДЕ. Если перенести это условие в секцию ГДЕ, левое соединение превратится во внутреннее, и мы потеряем элементы без заполненных реквизитов.
  3. В секции ГДЕ мы проверяем значение. Условие ЕСТЬ NULL гарантирует, что в выборку попадут и те партнеры, у которых данное свойство вообще отсутствует в табличной части. Альтернативный вариант написания этого условия — использование функции ЕСТЬNULL:

ГДЕ
    ЕСТЬNULL(ПартнерыДополнительныеРеквизиты.Значение, ЛОЖЬ) = ЛОЖЬ

Решение 2. Отбор по нескольким дополнительным реквизитам

Рано или поздно перед разработчиком встает задача: как сделать отбор по двум, трем или более свойствам одновременно? Например, нам нужны партнеры, у которых "Реквизит А" равен ЛОЖЬ, а "Реквизит Б" равен ИСТИНА.

Простое и очевидное решение, которое часто приходит на ум — добавить еще одно левое соединение к той же табличной части:


ВЫБРАТЬ
    Партнеры.Ссылка КАК Ссылка
ИЗ
    Справочник.Партнеры КАК Партнеры
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партнеры.ДополнительныеРеквизиты КАК ДопРеквизитА
        ПО Партнеры.Ссылка = ДопРеквизитА.Ссылка 
        И ДопРеквизитА.Свойство = &СвойствоА
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партнеры.ДополнительныеРеквизиты КАК ДопРеквизитБ
        ПО Партнеры.Ссылка = ДопРеквизитБ.Ссылка 
        И ДопРеквизитБ.Свойство = &СвойствоБ
ГДЕ
    ЕСТЬNULL(ДопРеквизитА.Значение, ЛОЖЬ) = ЛОЖЬ
    И ЕСТЬNULL(ДопРеквизитБ.Значение, ЛОЖЬ) = ИСТИНА

Внимание: хотя этот запрос является рабочим и простым для понимания, при увеличении количества соединяемых свойств (более 3-х) производительность СУБД может лавинообразно снизиться. Каждое новое левое соединение заставляет базу данных выполнять тяжелую операцию сопоставления таблиц. На больших базах данных (сотни тысяч партнеров) такой запрос начнет серьезно "тормозить".

Оптимальный подход для множественного отбора (свертка и транспонирование)

Чтобы избежать падения производительности, мы можем применить профессиональный подход: собрать все необходимые свойства во временную таблицу за один проход по табличной части дополнительных реквизитов, транспонировать (развернуть) строки в колонки с помощью группировки и конструкции ВЫБОР, а затем соединить результат с основной таблицей.

Рассмотрим этот эффективный алгоритм:


// Шаг 1: Выбираем нужные свойства во временную таблицу за один проход
ВЫБРАТЬ
    ДопРеквизиты.Ссылка КАК Партнер,
    МАКСИМУМ(ВЫБОР КОГДА ДопРеквизиты.Свойство = &СвойствоА ТОГДА ДопРеквизиты.Значение ИНАЧЕ NULL КОНЕЦ) КАК ЗначениеА,
    МАКСИМУМ(ВЫБОР КОГДА ДопРеквизиты.Свойство = &СвойствоБ ТОГДА ДопРеквизиты.Значение ИНАЧЕ NULL КОНЕЦ) КАК ЗначениеБ
ПОМЕСТИТЬ ВТ_СвойстваПартнеров
ИЗ
    Справочник.Партнеры.ДополнительныеРеквизиты КАК ДопРеквизиты
ГДЕ
    ДопРеквизиты.Свойство В (&СвойствоА, &СвойствоБ)
СГРУППИРОВАТЬ ПО
    ДопРеквизиты.Ссылка
;

// Шаг 2: Выполняем одно легкое левое соединение с подготовленными данными
ВЫБРАТЬ
    Партнеры.Ссылка КАК Ссылка
ИЗ
    Справочник.Партнеры КАК Партнеры
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СвойстваПартнеров КАК СвойстваПартнеров
        ПО Партнеры.Ссылка = СвойстваПартнеров.Partner
ГДЕ
    ЕСТЬNULL(СвойстваПартнеров.ЗначениеА, ЛОЖЬ) = ЛОЖЬ
    И ЕСТЬNULL(СвойстваПартнеров.ЗначениеБ, ЛОЖЬ) = ИСТИНА

Этот способ работает значительно быстрее, поскольку СУБД считывает данные из табличной части дополнительных реквизитов ровно один раз, отсекая все лишние свойства еще на этапе предварительного отбора.

Важное различие: Дополнительные реквизиты vs Дополнительные сведения

При работе с механизмами свойств БСП важно понимать разницу между двумя понятиями:

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


ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеСведения КАК ДопСведения
    ПО Партнеры.Ссылка = ДопСведения.Объект 
    И ДопСведения.Свойство = &Свойство

Использование механизма «Характеристики» в СКД

Если вы разрабатываете отчет на базе Системы компоновки данных (СКД), то ручное прописание связей в запросе — это прошлый век и плохая практика. Платформа 1С предоставляет мощный встроенный инструмент под названием "Характеристики".

Достаточно выполнить простую настройку на вкладке "Характеристики" в схеме компоновки данных:

  1. Укажите тип объекта (например, СправочникСсылка.Партнеры).
  2. Задайте источник видов характеристик (План видов характеристик ДополнительныеРеквизитыИСведения).
  3. Задайте источник значений характеристик (табличная часть Справочник.Партнеры.ДополнительныеРеквизиты, где ключевыми полями будут Свойство и Значение).

После этой минутной настройки пользователи вашего отчета смогут добавлять любые дополнительные реквизиты в группировки, отборы и поля отчета точно так же, как если бы это были стандартные реквизиты справочника. При этом СКД сама сформирует максимально оптимизированный SQL-запрос динамически.

Опасность разыменования через две точки в запросах

Напоследок разберем частую ошибку оптимизации. Никогда не пишите в условиях запроса обращения к реквизитам свойств через точку, например: ПартнерыДополнительныеРеквизиты.Свойство.Имя = "УровеньДоверия".

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

Правильный и профессиональный подход — всегда находить нужное свойство программно в коде 1С и передавать готовую ссылку в качестве параметра запроса:


СвойствоБлокировка = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "ИмяНужногоСвойства");

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ГДЕ ... Свойство = &Свойство";
Запрос.УстановитьПараметр("Свойство", СвойствоБлокировка);

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

← На главную