При разработке отчетов на Схеме компоновки данных (СКД) разработчики часто сталкиваются с непонятным поведением платформы. Один и тот же запрос, запущенный в обычной консоли запросов, возвращает корректные суммированные показатели. Однако стоит перенести его в СКД и сформировать отчет, как вместо ожидаемых итогов выводятся единицы, нули или странные небольшие числа. Создается впечатление, что система выводит количество строк или детальные признаки вместо реальных математических сумм.
В этой статье мы подробно разберем, почему возникает этот эффект, заглянем «под капот» оптимизатора СКД и рассмотрим три практических способа решения этой проблемы.
Для того чтобы понять, где именно «порылась собака», проанализируем, как СКД обрабатывает исходный текст запроса. Когда в наборе данных включено свойство Автозаполнение, СКД не выполняет запрос в его первозданном виде. Платформа анализирует структуру компоновщика, выбранные поля и группировки в настройках отчета, после чего динамически перестраивает SQL-запрос перед отправкой в СУБД. Это делается для оптимизации: система отсекает неиспользуемые временные таблицы, соединения и поля.
Если в вашем отчете возникают единицы и нули вместо сумм, вы столкнулись с одной из двух архитектурных особенностей СКД:
ВЫБОР КОГДА... ТОГДА 1 ИНАЧЕ 0) не добавлено на вкладку «Ресурсы» схемы компоновки, СКД считает его обычным нересурсным полем (измерением или реквизитом). Если в настройках отчета выводится группировка (например, по менеджеру), СКД автоматически добавляет все выбранные нересурсные числовые поля в секцию СГРУППИРОВАТЬ ПО (GROUP BY) результирующего SQL-запроса. СУБД группирует данные по уникальным комбинациям менеджера и признаков (1 и 0), возвращая детальные строки с единицами и нулями вместо итоговой суммы.ОБЪЕДИНИТЬ ВСЕ) и промежуточных таблиц СКД видит, что вы пытаетесь группировать данные и в самом запросе, и в настройках вывода отчета. Пытаясь оптимизировать вычисления, компоновщик может проигнорировать вашу финальную группировку в запросе, обратиться напрямую к детальным записям объединяемых таблиц и сгруппировать их по своим внутренним алгоритмам. Без явно указанных ресурсов это гарантированно приводит к потере агрегирования.Проанализируем детально структуру запроса, с которым возникли сложности у автора темы:
ВЫБРАТЬ
втОбщая.РабочийЛистМенеджер,
СУММА(втОбщая.ТелефонныйЗвонок_1) КАК ТелефонныйЗвонок_1,
СУММА(втОбщая.КонтактыПоТелефону_2) КАК КонтактыПоТелефону_2,
...
ИЗ
втОбщая КАК втОбщая
СГРУППИРОВАТЬ ПО
втОбщая.РабочийЛистМенеджер
В консоли запросов группировка по менеджеру отрабатывает идеально. Но в СКД, если поля ТелефонныйЗвонок_1 и КонтактыПоТелефону_2 не объявлены ресурсами, оптимизатор превращает этот блок в детальную выборку без суммирования, группируя строки по значениям этих самых единиц и нулей.
---
Самый простой, быстрый и методологически правильный способ исправить поведение отчета — явно указать СКД, что числовые колонки являются ресурсами, которые необходимо суммировать при любых группировках.
Выполним следующие шаги по настройке схемы:
ТелефонныйЗвонок_1, КонтактыПоТелефону_2, ВизитыПослеЗвонка_3 и так далее) и перенесем их в правое окно с помощью кнопки со стрелкой (или двойным кликом).СУММА(ИмяПоля). При необходимости выражение можно отредактировать вручную.После этой несложной настройки СКД поймет, что данные поля нельзя использовать в качестве ключей группировки SQL-запроса, уберет их из секции СГРУППИРОВАТЬ ПО и применит к ним функцию суммирования на уровне СУБД. Отчет мгновенно примет правильный вид.
---
Проектирование тяжелых группировок и итогов внутри самого текста запроса для СКД считается плохой практикой (anti-pattern). Это лишает систему гибкости и перегружает СУБД двойной работой. Проанализируем, как можно оптимизировать исходный пакет запросов.
Рекомендуется упростить финальный запрос пакета, убрав из него искусственную группировку, и передать «плоскую» таблицу детальных записей на обработку компоновщику:
СУММА() и секцию СГРУППИРОВАТЬ ПО. Запрос должен просто выбирать поля из временной таблицы:
ВЫБРАТЬ
втОбщая.РабочийЛистМенеджер КАК РабочийЛистМенеджер,
втОбщая.ТелефонныйЗвонок_1 КАК ТелефонныйЗвонок_1,
втОбщая.КонтактыПоТелефону_2 КАК КонтактыПоТелефону_2,
втОбщая.ВизитыПослеЗвонка_3 КАК ВизитыПослеЗвонка_3
ИЗ
втОбщая КАК втОбщая
ТелефонныйЗвонок_1, КонтактыПоТелефону_2 и настроим для них выражение СУММА(ТелефонныйЗвонок_1).РабочийЛистМенеджер.При таком подходе СКД сама сформирует максимально эффективный SQL-запрос к СУБД, выберет только нужные записи и мгновенно сгруппирует их в оперативной памяти сервера приложений. Это не только решит проблему с единицами, но и существенно ускорит работу отчета на больших базах данных.
---
Если по какой-то причине вам критически важно выполнить SQL-запрос в исходном виде, «один в один», без малейшего вмешательства со стороны механизмов СКД (например, в запросе используются сложные специфические конструкции СУБД или тонко настроенные индексы), можно отключить автоматическое преобразование запроса.
Разберем, как отключить оптимизатор СКД:
Данный метод используется редко и требует от разработчика высокой квалификации, однако он гарантирует, что СУБД выполнит именно тот текст запроса, который вы написали в редакторе.
---
Чтобы окончательно развеять сомнения и убедиться в том, как именно СКД преобразует ваш код, мы можем программно перехватить макет компоновки и посмотреть итоговый текст запроса. Сделать это можно с помощью внешней обработки «Консоль компоновки данных» или написав несколько строк кода в модуле отчета:
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
// В переменной МакетКомпоновки содержится реальный запрос, отправляемый в СУБД
Для Каждого НаборДанных Из МакетКомпоновки.НаборыДанных Цикл
Если ТипЗнч(НаборДанных) = Тип("НаборДанныхЗапросМакетаКомпоновкиДанных") Тогда
ЗаписьЖурналаРегистрации("РеальныйЗапросСКД", УровеньЖурналаРегистрации.Информация, , , НаборДанных.Запрос);
КонецЕсли;
КонецЦикла;
Запустив этот код в отладчике, мы наглядно увидим, как СКД превращает нашу финальную группировку в обычную детальную выборку с перечислением единиц и нулей в секции GROUP BY, если поля не были определены как ресурсы. Это знание убережет нас от подобных ошибок при проектировании сложных отчетов в будущем.