Перенос отлаженного запроса из обычной консоли запросов в Систему компоновки данных (СКД) — стандартная практика для разработчиков 1С. Однако очень часто при этом возникает неприятная ситуация: в консоли запрос отрабатывает идеально и возвращает нужные строки, а в СКД отчет формируется абсолютно пустым или начинает некорректно «двоить» и суммировать остатки.
В этой статье мы подробно разберем, почему возникают эти проблемы, как устроен внутренний механизм оптимизации СКД и как правильно настроить схему компоновки данных, чтобы ваш отчет работал быстро и выдавал верные цифры.
---
Разберем ситуацию, когда вы только что скопировали рабочий текст запроса в набор данных СКД, нажали кнопку «Сформировать», но на выходе получили абсолютно пустой табличный документ.
Выясним причину этого поведения. Когда мы создаем новый отчет в конструкторе СКД с нуля, платформа автоматически генерирует базовую структуру на вкладке «Настройки». Обычно туда помещается стандартная группировка Детальные записи.
Если же мы просто вставили сложный запрос в набор данных вручную, дерево настроек остается пустым. СКД успешно выполняет запрос в СУБД, получает выборку данных, но «не знает», в каком виде их нужно вывести пользователю. Без указания группировок система просто не будет выводить строки.
Решение проблемы:
Детальные записи.---
В отличие от обычной консоли запросов, которая выполняет SQL-код «как есть», СКД перед отправкой запроса на сервер СУБД полностью перестраивает его (компонует). Программа анализирует, какие поля выбрал пользователь в настройках, какие отборы применил и какие группировки задействовал.
Если в вашем пакете запросов есть временные таблицы (например, втОстатки, втЗаявлено), которые соединяются между собой по ключевым полям (например, по полю Номенклатура), но само поле Номенклатура не выбрано пользователем в финальных настройках отчета, оптимизатор СКД может посчитать это поле лишним и полностью «вырезать» его из промежуточных запросов во избежание нагрузки на базу данных. Это приведет к разрушению связей между временными таблицами и пустым результатам.
Решение проблемы:
Для всех полей, которые участвуют в соединениях связей или являются ключевыми для построения логики пакета запросов, необходимо принудительно отключить оптимизацию. Для этого перейдем на вкладку «Наборы данных», найдем нужные поля (например, ключевые измерения) и установим для них флаг «Обязательное». Это гарантирует, что СКД не исключит их из SQL-запроса при компоновке.
---
Это самая распространенная проблема при работе с бухгалтерскими регистрами и регистрами накопления остатков. Автор темы на форуме столкнулся с тем, что при очистке ролей у полей остатков отчет стал работать «правильно», но как только в выборку попало более одного товара, расчет полностью сломался.
Проанализируем, почему это происходит. Поля начального и конечного остатка (например, КоличествоНачальныйОстаток, КоличествоКонечныйОстаток) — это не обычные числовые обороты. Если сложить остаток на понедельник (10 шт.) и остаток на вторник (10 шт.), мы не получим остаток в 20 шт. Остаток на конец периода должен соответствовать значению на последнюю дату, а не сумме за все дни.
Когда мы убираем роли остатков в СКД, система начинает считать их простыми ресурсами и банально суммирует по всем строкам группировок. На выходе получаются огромные, не имеющие отношения к реальности цифры.
Чтобы СКД рассчитывала остатки корректно по всем периодам и иерархиям, выполним по шагам настройку ролей полей на вкладке «Наборы данных»:
КоличествоНачальныйОстаток), дважды кликнем по колонке «Роль» и настроим параметры:
КоличествоКонечныйОстаток).Количество).КоличествоКонечныйОстаток) и настроим его роль аналогично:
КоличествоНачальныйОстаток).Количество).Номенклатура, Организация, Склад) в этой же роли обязательно укажем их как Измерения. Это укажет системе, в разрезе каких аналитик рассчитываются данные остатки.День, Неделя, Месяц) настроим роль Период и укажем их порядковый номер (для дня — 1, для недели — 2, для месяца — 3), чтобы СКД понимала временную шкалу и правильно распределяла остатки по оси времени.---
Рассмотрим фрагмент запроса из сообщения 15 форума:
ИЗ РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПеремещениеТоваров.Товары КАК ПеремещениеТоваровТовары
ПО Хозрасчетный.Регистратор = ПеремещениеТоваровТовары.Ссылка
Подобная конструкция соединения физической таблицы регистра с табличной частью документа по полю Регистратор крайне нежелательна и способна привести к полному зависанию базы данных на реальных объемах.
Поле Регистратор имеет составной тип данных. При выполнении такого запроса СУБД генерирует внутренние неявные соединения (LEFT JOIN) абсолютно со всеми таблицами документов, которые могут являться регистраторами для данного регистра бухгалтерии.
Как правильно оптимизировать это соединение:
Если мы заранее знаем, что нас интересуют только документы определенного вида (например, ПеремещениеТоваров), мы должны жестко ограничить тип регистратора с помощью конструкции ВЫРАЗИТЬ():
ПО ВЫРАЗИТЬ(Хозрасчетный.Регистратор КАК Документ.ПеремещениеТоваров) = ПеремещениеТоваровТовары.Ссылка
Это позволит СУБД построить оптимальный план запроса и соединить таблицы напрямую, минуя сканирование десятков других таблиц документов.
Еще более надежный способ — предварительно выбрать все нужные движения регистра во временную таблицу с явным отбором по типу регистратора в секции ГДЕ, и только затем соединять готовую плоскую выборку с табличной частью документа.
---
Часто разработчики тратят много времени на написание кода заполнения параметров в модуле формы отчета, как в примере из сообщения 20:
ПараметрСчет = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("Счет");
ПараметрСчет.Использование = Истина;
ПараметрСчет.Значение = ПланыСчетов.Хозрасчетный.ТоварыНаСкладах;
Этого рутинного шага можно легко избежать. В языке запросов СКД поддерживается обращение к предопределенным элементам конфигурации напрямую через конструкцию ЗНАЧЕНИЕ(). Это избавляет от необходимости писать код заполнения параметров на форме и делает схему отчета полностью автономной.
Заменим передачу параметров в запросе на использование литералов:
Хозрасчетный.СчетДт = &Счет запишем:
Хозрасчетный.СчетДт = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ТоварыНаСкладах)
ЗаявкиПродажи.ВидДвиженияТовара = &ВидыДвиженияТовара_Заявка запишем:
ЗаявкиПродажи.ВидДвиженияТовара = ЗНАЧЕНИЕ(Перечисление.ВидыДвиженияТовара.Заявка)
Такой подход делает схему компоновки переносимой, чистой и значительно ускоряет разработку и отладку отчета.