При разработке отчетов на схеме компоновки данных (СКД) разработчики часто сталкиваются с необходимостью объединения информации из разных источников. Типичный пример — вывод в одном отчете данных по выработке транспортных средств (из путевых листов) и затрат на их обслуживание (из ремонтных листов). Для реализации этой задачи используется механизм «Набор данных - объединение».
Однако при добавлении параметров фильтрации по дате в оба запроса объединения возникает популярная проблема: в системе появляется множество дублирующих параметров (например, начало и конец периода для каждого запроса отдельно), а сам отчет начинает выводить пустые строки (визуальные разрывы с NULL значениями). Рассмотрим подробное руководство, как профессионально решить эти проблемы, оптимизировать производительность запросов и сделать интерфейс выбора периода максимально удобным для пользователя.
Разберем типичную ошибку, с которой столкнулся автор темы. При попытке наложить условие на дату документа через табличную часть (например, уатРемонтныйЛистМатериалы.Ссылка.Дата), система может выдать ошибку отсутствия поля или существенно снизить производительность отчета.
Обращение к реквизитам шапки документа через точку от табличной части (Материалы.Ссылка.Дата) в секции ГДЕ заставляет СУБД выполнять неявное левое соединение (LEFT JOIN). На базах с большим объемом данных это приводит к катастрофическому падению скорости генерации отчета. Чтобы этого избежать, мы применим явное внутреннее соединение табличной части с шапкой документа.
Рассмотрим правильную структуру первого запроса (выработка ТС):
ВЫБРАТЬ
уатПутевойЛистЗадание.КоличествоЧасов КАК КоличествоЧасов,
уатПутевойЛистЗадание.Ссылка.ТранспортноеСредство КАК ТС,
уатПутевойЛистЗадание.ОбъектСтроительства КАК ОбъектСтроительства,
уатПутевойЛист.Дата КАК Дата
ИЗ
Документ.уатПутевойЛист.Задание КАК уатПутевойЛистЗадание
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.уатПутевойЛист КАК уатПутевойЛист
ПО уатПутевойЛистЗадание.Ссылка = уатПутевойЛист.Ссылка
ГДЕ
уатПутевойЛист.Дата МЕЖДУ &НачалоПериода И &КонецПериода
Теперь проанализируем второй запрос (затраты на ремонт) и построим его по аналогичному производительному принципу:
ВЫБРАТЬ
уатРемонтныйЛистМатериалы.Сумма КАК Сумма,
уатРемонтныйЛистМатериалы.Ссылка.ТС КАК ТС,
уатРемонтныйЛист.Дата КАК Дата
ИЗ
Документ.уатРемонтныйЛист.Материалы КАК уатРемонтныйЛистМатериалы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.уатРемонтныйЛист КАК уатРемонтныйЛист
ПО уатРемонтныйЛистМатериалы.Ссылка = uatРемонтныйЛист.Ссылка
ГДЕ
уатРемонтныйЛист.Дата МЕЖДУ &НачалоПериода И &КонецПериода
Выясним причину, почему в СКД изначально появляется 4 параметра вместо 2. Если в первом запросе написать &НачПериода и &КонПериода, а во втором — &ДатаНачала и &ДатаОкончания, компоновщик данных воспримет их как абсолютно разные сущности. На форме отчета пользователю придется заполнять четыре поля дат.
Золотое правило СКД: чтобы параметры из разных запросов внутри схемы компоновки объединились в один общий параметр, они должны иметь абсолютно одинаковое написание (с учетом регистра букв). Мы назвали их одинаково в обоих запросах: &НачалоПериода и &КонецПериода. Теперь СКД автоматически объединит их на вкладке «Параметры».
Если пользователь захочет сформировать отчет за все время, не указывая период, стандартный запрос с оператором МЕЖДУ выдаст ошибку или пустой результат. Чтобы сделать параметры гибкими, мы используем конструкцию расширения языка запросов СКД с фигурными скобками {}.
Перепишем условия фильтрации в наших запросах:
// Для первого запроса:
{ГДЕ (уатПутевойЛист.Дата >= &НачалоПериода), (уатПутевойЛист.Дата <= &КонецПериода)}
// Для второго запроса:
{ГДЕ (уатРемонтныйЛист.Дата >= &НачалоПериода), (уатРемонтныйЛист.Дата <= &КонецПериода)}
Если параметры периода не заполнены, СКД просто проигнорирует эти строки при построении итогового SQL-запроса к базе данных.
Для конечного пользователя неудобно выбирать даты начала и окончания в двух разных полях календаря. Гораздо удобнее использовать стандартный системный диалог выбора периода (например, «Текущий месяц», «Прошлый квартал»). Выполним по шагам настройку этого механизма в СКД:
ПериодОтчета. Установим для него понятный заголовок — «Период».СтандартныйПериод.НачалоПериода и КонецПериода установим флажок в колонке «Ограничение доступности». Это скроет их от пользователя на форме настроек, чтобы избежать путаницы.НачалоПериода пропишем связь: &ПериодОтчета.ДатаНачала.КонецПериода укажем: КонецПериода(&ПериодОтчета.ДатаОкончания, "ДЕНЬ").Важное примечание: использование функции КонецПериода крайне важно! Если пользователь выберет период, например, по сегодняшний день, то без этой функции граница установится на 00:00:00 начала дня. Все документы, проведенные в течение сегодняшнего дня, просто не попадут в отчет. Указание параметра "ДЕНЬ" сдвигает время границы на 23:59:59.
При объединении наборов данных из разных документов часто возникает проблема «рваных» строк. Например, в одной строке выводится транспортное средство и его выработка, а сумма ремонта пустая. В следующей строке — то же транспортное средство, пустая выработка, но заполнена сумма ремонта. Использование условного оформления (скрытие строк по условию видимости) является «костылем», который не решает проблему расхода памяти и некорректного расчета итогов.
Рассмотрим правильные архитектурные способы борьбы с этой проблемой:
ТС. Если в одном наборе оно называется ТС, а в другом — ТранспортноеСредство, СКД не сможет их связать и разведет по разным колонкам и строкам.ТС, а числовые поля (КоличествоЧасов, Сумма) добавьте на вкладку «Ресурсы» с функцией Сумма(...). Компоновщик автоматически «схлопнет» данные по каждому автомобилю в одну красивую строку.ТС) откройте настройки в колонке «Роль». Установите флажок «Игнорировать NULL». Это заставит систему игнорировать группировочные записи, не имеющие ссылочной целостности по данному полю.Применяя данные подходы, вы получите быстро работающий, визуально чистый и понятный для пользователей отчет, построенный на базе объединения разнородных данных.