Как в СКД настроить отбор по партнерам, не входящим ни в один сегмент?

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

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

Почему стандартный отбор "Не заполнено" не работает?

Проанализируем архитектуру хранения данных в типовых конфигурациях, таких как 1С:Управление торговлей 11, ERP или Комплексная автоматизация. В отличие от простых реквизитов (например, "Основной менеджер" или "Бизнес-регион"), информация о принадлежности партнера к сегментам не хранится непосредственно в справочнике Партнеры. Для этого используется независимый регистр сведений ПартнерыСегмента.

Рассмотрим ситуацию с точки зрения SQL-запроса, который генерирует СКД. Когда мы добавляем отбор по сегменту, система чаще всего пытается найти соответствие в таблице регистра. Если партнер не входит ни в один сегмент, записи о нем в этом регистре просто не существует. Вид сравнения Не заполнено технически предназначен для проверки поля на значение NULL или пустую ссылку. Но так как записи в регистре нет физически, условие "поле равно пустому значению" не срабатывает — строка целиком исключается из выборки еще на этапе соединения таблиц.

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

Этот метод считается наиболее "чистым", так как он позволяет использовать стандартные возможности платформы без написания сложного кода. Мы заставим СКД воспринимать сегменты как виртуальные реквизиты партнера. Рассмотрим по шагам, как это настроить:

  1. Откроем схему компоновки данных и перейдем на вкладку "Настройки характеристик" (находится на закладке "Объект" или "Наборы данных" в зависимости от версии платформы).
  2. В качестве источника видов характеристик укажем запрос к справочнику СегментыПартнеров.
  3. В качестве источника значений характеристик выберем регистр сведений ПартнерыСегмента.
  4. Настроим связь: Объект (в регистре) должен соответствовать полю Партнер (в нашем наборе данных).

После такой настройки в списке доступных полей для отбора появится виртуальное поле "Сегмент". Поскольку СКД теперь управляет этим полем через механизм характеристик, она сама сформирует ЛЕВОЕ СОЕДИНЕНИЕ. В этом случае проверка на "Не заполнено" начнет работать корректно, так как система увидит NULL там, где запись в регистре отсутствует.

Способ 2. Программная модификация запроса в ПриКомпоновкеРезультата

В типовых решениях часто используется программная установка отборов. Если нам нужно жестко контролировать поведение отчета, мы можем вмешаться в процесс компоновки. Рассмотрим пример кода, который можно разместить в модуле объекта отчета в процедуре ПриКомпоновкеРезультата:


Процедура ПриКомпоновкеРезультата(РезультатСформирован)
    
    РезультатСформирован = Истина;
    
    // Получаем текущие настройки отчета
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    
    // Ищем элемент отбора по полю "Сегмент"
    ПолеСегмент = Новый ПолеКомпоновкиДанных("Сегмент");
    Для Каждого ЭлементОтбора Из Настройки.Отбор.Элементы Цикл
        
        Если ТипЗнч(ЭлементОтбора) = Тип("ЭлементОтбораКомпоновкиДанных") 
            И ЭлементОтбора.ЛевоеЗначение = ПолеСегмент Тогда
            
            // Проверяем, выбрано ли условие "Не заполнено"
            Если ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.НеЗаполнено Тогда
                
                // Здесь мы можем программно изменить текст запроса или
                // добавить условие НЕ В (ВЫБРАТЬ Партнер ИЗ РегистрСведений.ПартнерыСегмента)
                ЭлементОтбора.Использование = Ложь; // Отключаем стандартный отбор
                
                // Добавляем свое условие через параметр или вложенный запрос
                ДобавитьПрограммноеУсловиеНаОтсутствиеСегмента(Настройки);
                
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;
    
    // Выполняем стандартную компоновку с измененными настройками
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(Результат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
КонецПроцедуры

Выясним причину, почему это эффективно: мы перехватываем намерение пользователя и подменяем нерабочее стандартное сравнение на корректный подзапрос ГДЕ НЕ Ссылка В (ВЫБРАТЬ Партнер ИЗ РегистрСведений.ПартнерыСегмента).

Способ 3. Создание вычисляемого поля (Флаг наличия сегмента)

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

Разберем по шагам реализацию через запрос:

  1. В основном запросе набора данных добавим ЛЕВОЕ СОЕДИНЕНИЕ со справочником партнеров и регистром сегментов.
  2. Добавим в список полей запроса выражение:
    
    ВЫБОР
        КОГДА ПартнерыСегмента.Сегмент ЕСТЬ NULL ТОГДА ЛОЖЬ
        ИНАЧЕ ИСТИНА
    КОНЕЦ КАК ВходитВЛюбойСегмент
    
  3. В настройках СКД назовем это поле "Входит в любой сегмент".

Теперь пользователь вместо неработающего отбора "Сегмент Не заполнено" может просто поставить фильтр "Входит в любой сегмент Равно Ложь". Это интуитивно понятно и гарантированно работает на уровне базы данных.

Нюансы производительности при работе с большими базами

Важно помнить, что использование конструкции НЕ В (ВЫБРАТЬ...) может существенно замедлить формирование отчета, если в базе сотни тысяч партнеров. Посмотрим на два варианта оптимизации:

  1. Индексация: Убедитесь, что в регистре ПартнерыСегмента поле Партнер является ведущим или индексированным (в типовых конфигурациях это так по умолчанию).
  2. Временные таблицы: Если отчет сложный, сначала выберите всех партнеров, имеющих сегменты, во временную таблицу, а затем основным запросом выберите тех партнеров, которых нет в этой временной таблице.

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


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ПартнерыСегмента.Партнер КАК Партнер
ПОМЕСТИТЬ ВТ_ПартнерыССегментами
ИЗ
    РегистрСведений.ПартнерыСегмента КАК ПартнерыСегмента
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Партнеры.Ссылка КАК Партнер
ИЗ
    Справочник.Партнеры КАК Партнеры
ГДЕ
    НЕ Партнеры.Ссылка В (ВЫБРАТЬ ВТ.Партнер ИЗ ВТ_ПартнерыССегментами КАК ВТ)

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

Особенности для динамических сегментов

Проанализируем еще один важный момент: в 1С:УТ 11 сегменты могут быть динамическими (формируемыми по правилам запроса). Данные о таких сегментах не хранятся в регистре ПартнерыСегмента постоянно.

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

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

← На главную