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

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

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

Выясним причину некорректной работы пакета запросов в СКД

Главная причина расхождения между результатом консоли запросов и отчетом на СКД заключается в работе встроенного оптимизатора СКД:

  1. Механизм автозаполнения и удаление «неиспользуемых» полей. При включенном свойстве набора данных Автозаполнение компоновщик анализирует только те поля, которые выбраны в текущих группировках, колонках или отборах настроек отчета. Если промежуточное поле во временной таблице используется только в условии соединения или подзапросе вида ГДЕ Поле В (ВЫБРАТЬ ...), оптимизатор СКД может ошибочно посчитать его лишним и вырезать из итогового запроса.
  2. Неявные зависимости во вложенных конструкциях. Использование конструкций выборки из временных таблиц внутри параметров виртуальных таблиц (например, в параметре условия регистра Регистр.ОстаткиИОбороты(..., ЗаказКлиента В (ВЫБРАТЬ ...))) затрудняет для СКД построение графа связей. В результате связующие поля во временных таблицах удаляются, что приводит к разрушению логики соединений.
  3. Ограничения механизма RLS (ограничение прав на уровне записей). Использование вложенных подзапросов ВЫБРАТЬ ... ИЗ (ВЫБРАТЬ ...) не позволяет применить конструкцию РАЗРЕШЕННЫЕ внутри вложенного запроса. Это вызывает ошибки доступа или вывод «Объект не найден» у пользователей с неполными правами. При переходе на пакет запросов с временными таблицами синтаксис ВЫБРАТЬ РАЗРЕШЕННЫЕ становится доступным, но вмешивается оптимизатор СКД.

Способ 1. Установка признака обязательности для полей и таблиц

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

Выполним следующие шаги:

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

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

Оптимизатор СКД значительно лучше обрабатывает явные соединения между таблицами, чем конструкции В (ВЫБРАТЬ ...) в параметрах виртуальных таблиц. Перепишем выборку с использованием явных левых и внутренних соединений.

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


ВЫБРАТЬ
    ЗаказыКлиентовОстаткиИОбороты.ЗаказКлиента КАК ЗаказКлиента,
    ИСТИНА КАК ПолностьюОтгружен,
    ЗаказыКлиентовОстаткиИОбороты.ЗаказаноРасход КАК ЗаказаноРасход
ПОМЕСТИТЬ ВТ
ИЗ
    РегистрНакопления.ЗаказыКлиентов.ОстаткиИОбороты(&ДатаНач, &ДатаКон, , , ) КАК ЗаказыКлиентовОстаткиИОбороты
ГДЕ
    ЗаказыКлиентовОстаткиИОбороты.ЗаказаноКонечныйОстаток = 0

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

ВЫБРАТЬ
    РасчетыСКлиентамиОбороты.ЗаказКлиента,
    ЛОЖЬ,
    0
ИЗ
    РегистрНакопления.РасчетыСКлиентами.Обороты(&ДатаНач, &ДатаКон, , ЗаказКлиента ССЫЛКА Документ.ЗаказКлиента) КАК РасчетыСКлиентамиОбороты
ГДЕ
    РасчетыСКлиентамиОбороты.СуммаРасход > 0
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.ЗаказКлиента КАК ЗаказКлиента,
    МАКСИМУМ(ВТ.ПолностьюОтгружен) КАК ПолностьюОтгружен,
    СУММА(ВТ.ЗаказаноРасход) КАК ЗаказаноРасход
ПОМЕСТИТЬ ВТЗаказы
ИЗ
    ВТ КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.ЗаказКлиента
ИНДЕКСИРОВАТЬ ПО
    ЗаказКлиента
;

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

Способ 3. Отключение автозаполнения и ручное управление полями

Если оптимизатор СКД продолжает искажать итоговый текст запроса, отключим автоматическую модификацию:

  1. В наборе данных типа «Запрос» снимем флаг Автозаполнение.
  2. Вручную опишем доступные поля для выбора, группировки и отборов с помощью фигурных скобок СКД: {ВЫБРАТЬ ...}, {ГДЕ ...}.
  3. В таком режиме СКД выполнит пакет запросов в исходном виде без принудительного вырезания секций и полей.

Способ 4. Программное формирование данных

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

  1. Через Менеджер временных таблиц: формируем тяжелые временные таблицы в коде через МенеджерВременныхТаблиц, после чего передаем менеджер в процессор компоновки данных:
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборы, ДанныеРасшифровки, Истина, , МенеджерВТ);
    
  2. Через внешний набор данных: выполняем весь пакет запросов обычным объектом Запрос, выгружаем итоговый результат в ТаблицаЗначений и передаем ее в СКД через коллекцию ВнешниеНаборыДанных.

Рекомендация по диагностике

Чтобы точно увидеть, как именно СКД модифицирует исходный запрос, воспользуемся специализированными инструментами (например, подсистемой Инструменты разработчика) или получим реальный выполняемый текст запроса программно из объекта МакетКомпоновкиДанных. Это позволит мгновенно определить, какое именно поле или временная таблица были вырезаны оптимизатором.

← На главную