Как правильно связать наборы данных в СКД по номенклатуре и характеристикам?

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

При разработке отчетов в системе компоновки данных (СКД) нередко возникает задача объединить несколько источников: например, справочник номенклатуры и регистры накопления (остатки, продажи). Если связать два набора данных одновременно по полям Номенклатура и Характеристика, разработчики часто сталкиваются с неприятным поведением: позиции, для которых учет по характеристикам отключен, просто исчезают из результирующего отчета.

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

В чем причина выпадения строк без характеристик?

Проанализируем механику работы с данными ссылочного типа в платформе «1С:Предприятие». Проблема кроется в несовпадении типов данных при соединении:

  1. В регистрах накопления: измерения ссылочного типа в виртуальных таблицах никогда не принимают значение NULL. Если учет по характеристикам для номенклатуры не ведется или характеристика не указана в документе-регистраторе, в регистр записывается значение пустой ссылки соответствующего типа — ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка).
  2. В справочниках при левом соединении: если первый набор данных формируется запросом с левым соединением справочников Номенклатура и ХарактеристикиНоменклатуры, то для позиций без признака ведения учета по характеристикам результат соединения полей подчиненного справочника возвращает значение NULL.

Когда система компоновки данных пытается сопоставить строки двух наборов по правилу Характеристика = Характеристика, в памяти выполняется сравнение: NULL = ПустаяСсылка. В реляционной алгебре и логике 1С результатом такого сравнения всегда является Ложь. В итоге строка отсекается, и номенклатура пропадает из отчета.

Попытки решить проблему «в лоб» склейкой строковых представлений через ВЫРАЗИТЬ(... КАК СТРОКА) работают некорректно, создают лишнюю нагрузку на сервер и являются грубой ошибкой разработки. Рассмотрим правильные варианты решения.

Решение 1. Приведение значений к пустой ссылке в запросе

Наиболее простое исправление на уровне исходных запросов — гарантировать, что в первом наборе данных вместо NULL всегда будет присутствовать пустая ссылка справочника характеристик. Для этого воспользуемся функцией ЕСТЬNULL.

Посмотрим на пример текста запроса для первого набора данных:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    ЕСТЬNULL(ХарактеристикиНоменклатуры.Ссылка, ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка)) КАК Характеристика,
    Номенклатура.ВестиУчетПоХарактеристикам КАК ВестиУчетПоХарактеристикам
ИЗ
    Справочник.Номенклатура КАК Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ХарактеристикиНоменклатуры КАК ХарактеристикиНоменклатуры
        ПО ХарактеристикиНоменклатуры.Владелец = Номенклатура.Ссылка

Обратим внимание на важную особенность современных типовых конфигураций (таких как «1С:ERP Управление предприятием», «Комплексная автоматизация» или «Управление торговлей»): владельцем характеристики может выступать не сама Номенклатура, а ВидНоменклатуры (когда используются общие характеристики для вида номенклатуры). В таких конфигурациях прямое соединение по владельцу-номенклатуре приведет к потере общих характеристик.

Второй вариант формирования выборки — явное разделение позиций с помощью конструкции ОБЪЕДИНИТЬ ВСЕ:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка) КАК Характеристика,
    Номенклатура.ВестиУчетПоХарактеристикам КАК ВестиУчетПоХарактеристикам
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    НЕ Номенклатура.ВестиУчетПоХарактеристикам

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    ХарактеристикиНоменклатуры.Ссылка КАК Характеристика,
    Номенклатура.ВестиУчетПоХарактеристикам КАК ВестиУчетПоХарактеристикам
ИЗ
    Справочник.ХарактеристикиНоменклатуры КАК ХарактеристикиНоменклатуры
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
        ПО ХарактеристикиНоменклатуры.Владелец = Номенклатура.Ссылка
ГДЕ
    Номенклатура.ВестиУчетПоХарактеристикам

Теперь оба набора данных содержат одинаковые типы значений: если характеристики нет, в обоих источниках возвращается Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка, и стандартная связь СКД по двум полям сработает корректно.

Решение 2. Использование колонки «Условие связи» в СКД

Платформа 1С предоставляет мощный встроенный инструмент на вкладке Связи наборов данных — колонку Условие связи. В ней можно задавать произвольные логические выражения на языке выражений компоновки данных, не меняя тексты самих запросов.

Разберем настройку по шагам:

  1. На вкладке Связи наборов данных настраиваем первую связь по номенклатуре: Источник связи — НаборДанных1, Приемник связи — НаборДанных2, Поле источника — Номенклатура, Поле приемника — Номенклатура.
  2. Добавляем вторую строку связи по полю Характеристика.
  3. В колонке Условие связи прописываем условие:

ВЫБОР 
    КОГДА Номенклатура.ВестиУчетПоХарактеристикам 
        ТОГДА Характеристика = Источник.Характеристика 
    ИНАЧЕ Истина 
КОНЕЦ

При таком подходе для номенклатуры без признака ведения учета условие по характеристике всегда будет принимать значение Истина, а связь выполнится только по полю Номенклатура.

Архитектурная альтернатива: один набор данных-запрос

Проанализируем производительность использования связей между наборами данных. Механизм связей наборов данных в СКД имеет два существенных недостатка:

  1. Запросы в цикле (проблема N+1): если во второй набор данных передаются параметры из первого (колонка Параметры), СКД выполняет запрос подчиненного набора порциями для каждой строки или группы строк родительского набора, что на больших объемах данных приводит к катастрофическому падению скорости.
  2. Перегрузка оперативной памяти: если параметры не передаются, подчиненный набор выгружается в память сервера 1С целиком, после чего платформа программно связывает таблицы.

В стандартах разработки 1С наиболее эффективным решением считается объединение остатков, оборотов и справочников в один набор данных-запрос с помощью конструкции ОБЪЕДИНИТЬ ВСЕ.

Посмотрим, как выглядит оптимальная структура единого запроса:


ВЫБРАТЬ
    ПродажиОбороты.Номенклатура КАК Номенклатура,
    ПродажиОбороты.Характеристика КАК Характеристика,
    ПродажиОбороты.КоличествоОборот КАК КоличествоПродаж,
    0 КАК КоличествоОстаток
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ПродажиОбороты

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    ТоварыНаСкладахОстатки.Характеристика КАК Характеристика,
    0 КАК КоличествоПродаж,
    ТоварыНаСкладахОстатки.ВНаличииОстаток КАК КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&ПериодОстатков, ) КАК ТоварыНаСкладахОстатки

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка) КАК Характеристика,
    0 КАК КоличествоПродаж,
    0 КАК КоличествоОстаток
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    НЕ Номенклатура.ЭтоГруппа

В этом запросе:

  1. Все данные собираются за одно обращение к СУБД.
  2. Показатели суммируются на уровне СКД с помощью настройки ресурсов (Сумма(КоличествоПродаж), Сумма(КоличествоОстаток)).
  3. Третья секция гарантирует вывод всей номенклатуры справочника (даже если по ней не было движений и остатков).
  4. Проблема сопоставления NULL и пустых ссылок полностью исключается, так как система компоновки данных выполняет группировку самостоятельно.

Использование единого набора данных-запроса обеспечивает максимальную скорость работы отчета и защищает от скрытых ошибок при соединении таблиц.

← На главную