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

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

При разработке сложных отчетов в системе компоновки данных (СКД) часто возникает необходимость объединения данных из разных источников. Классический пример — построение отчета по остаткам в разрезе всех периодов (например, месяцев), даже если в конкретном месяце движений или остатков не было. В таких случаях разработчики используют связь наборов данных, где ведущим набором выступает производственный календарь или список периодов, а ведомым — данные из регистров. Однако на практике часто встречается ситуация, когда строки с «пустыми» остатками просто исчезают из финального отчета. Разберем подробно, почему так происходит, как работает механизм связей Master-Detail и как устранить причины потери данных.

Механика связей наборов данных (Master-Detail) в СКД

Для начала проанализируем, как именно СКД обрабатывает связи между наборами. В отличие от стандартного оператора ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) в языке запросов SQL, связи в компоновке работают по принципу Master-Detail. Набор-источник (ведущий) определяет основной состав строк. Для каждой строки из источника система пытается найти соответствующие данные в приемнике по заданному условию связи.

Рассмотрим ситуацию: у нас есть Набор 1 (Список месяцев) и Набор 2 (Остатки из виртуальной таблицы ОстаткиИОбороты). Мы связываем их по полю Период. Логика СКД предполагает следующее:

  1. Система получает все записи из Набора 1.
  2. Для каждой записи Набора 1 она выполняет запрос к Набору 2, подставляя значение периода в качестве параметра.
  3. Если данные в Наборе 2 найдены, они дополняют строку.
  4. Если данных в Наборе 2 нет, поля этого набора заполняются значением NULL.

Казалось бы, если галочка «Обязательная связь» в настройках связей снята, строки из Набора 1 должны выводиться в любом случае. Но здесь кроется главная ловушка: влияние фильтров и отборов.

Проблема 1: Отборы по полям вторичного набора

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

Рассмотрим пример программной установки отбора в модуле объекта, который часто становится причиной ошибки:


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

Решение: Проанализируем все уровни настройки отбора. Если вам необходимо видеть все периоды, убедитесь, что ни в настройках варианта, ни в фиксированных настройках, ни в коде модуля нет фильтров по полям из набора с остатками. Если отбор все же нужен, его следует накладывать с учетом возможных NULL, например, используя условие «ИЛИ Поле НЕ ЗАПОЛНЕНО».

Проблема 2: Передача параметров в связанные наборы

Второй важный аспект — передача параметров. Когда мы связываем календарь и виртуальную таблицу остатков, мы передаем «Конец периода» из календаря в параметр виртуальной таблицы &Период. Посмотрим на структуру связи:

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

Оптимизация: Игнорировать значения NULL

В окне настройки связей наборов данных есть колонка «Игнорировать значения NULL». Разберем, на что она влияет. Если в ведущем наборе (календаре) по какой-то причине в поле связи (дата) оказалось значение NULL, и эта галка установлена, СКД просто проигнорирует эту строку и не будет пытаться делать запрос к приемнику. В нашей задаче (вывод всех месяцев) это обычно не является причиной проблемы, но об этом флаге стоит помнить при работе со справочниками.

Нюансы расчета ресурсов (итогов)

Когда мы используем несколько наборов данных, расчет ресурсов может вести себя специфично. Если вы хотите, чтобы в пустых месяцах выводились нули вместо пустоты, используйте функцию ЕстьNull() в выражении ресурса на вкладке «Ресурсы»:

Сумма(ЕстьNull(СуммаОстаток, 0))

Это обеспечит корректное отображение числовых данных в таблице отчета, даже если фактических записей в регистре не было.

Альтернативный подход: Объединение (UNION) вместо связей

Проанализируем производительность. Использование связей наборов данных (Dataset Links) заставляет СКД выполнять отдельный запрос к базе данных для каждой строки ведущего набора. Если вы выводите отчет за 10 лет по дням, это породит более 3600 запросов к БД, что крайне неэффективно.

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


ВЫБРАТЬ
    Календарь.Дата КАК Период
ПОМЕСТИТЬ ВТ_Периоды
ИЗ
    &ТаблицаПериодов КАК Календарь
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Периоды.Период,
    ЕСТЬNULL(ХозрасчетныйОстатки.СуммаОстаток, 0) КАК Остаток
ИЗ
    ВТ_Периоды КАК ВТ_Периоды
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.Хозрасчетный.Остатки(&На Дату, ...) КАК ХозрасчетныйОстатки
        ПО ВТ_Периоды.Период = ХозрасчетныйОстатки.Период

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

Выводы и рекомендации

Подводя итог, если у вас пропадают строки при связи наборов в СКД, выполните следующие шаги:

  1. Проверьте вкладку «Отбор» в настройках отчета — там не должно быть полей из ведомого набора.
  2. Изучите программный код в модуле объекта — проверьте, не устанавливаются ли там дополнительные фильтры в КомпоновщикНастроек.
  3. Убедитесь, что для связи используется правильный тип (ведущий набор — это календарь).
  4. Проверьте параметры виртуальных таблиц в наборе-приемнике.
  5. Для отображения нулей вместо пустых ячеек используйте ЕстьNull() в выражениях ресурсов.

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

← На главную