При разработке отчетов по посещаемости, графикам работ или продажам программисты часто сталкиваются с ситуацией, когда в информационной базе данные есть только за определенные дни. Если просто построить отчет по такому регистру, СКД выведет только те даты, по которым существуют записи. В итоге календарная сетка получается «дырявой». Наша задача — сделать так, чтобы в колонках отображались все дни выбранного периода (например, месяца), а там, где данных нет, стояли прочерки или пустые значения. Рассмотрим несколько эффективных способов реализации этой задачи. Для этой задачи также можно использовать пример формирования шапки отчета по неделям и месяцам в СКД.
Система компоновки данных обладает встроенным функционалом для генерации недостающих периодов. Чтобы этот механизм заработал, необходимо выполнить настройки на двух уровнях: в схеме компоновки и в настройках отчета.
Шаг 1: Настройка роли поля в наборе данных
В конструкторе схемы компоновки данных на закладке «Наборы данных» найдем поле, отвечающее за дату (например, Период или ДатаОтметки). Нам нужно правильно заполнить колонку «Роль» для этого поля:
День.Без настройки роли СКД не поймет, что этот период нужно дополнять программно.
Шаг 2: Настройка дополнения в варианте отчета
Перейдем на закладку «Настройки», выберем группировку по дням (которая обычно идет в колонках таблицы) и на вкладке «Дополнительные настройки» или непосредственно в настройках группировки (зависит от версии платформы) найдем свойства дополнения периода:
День.&ДатаНачала.&ДатаОкончания.Этот метод самый «чистый», так как он не требует изменения запроса, но он критичен к правильной настройке ролей полей.
Если штатное дополнение по каким-то причинам не подходит, мы можем самостоятельно создать таблицу со всеми датами месяца и соединить ее с нашими данными. Проанализируем интересный вариант генерации дат без использования реальных таблиц БД, основанный на декартовом произведении маленьких выборок.
Рассмотрим пример запроса, который генерирует последовательность чисел, преобразуемых в даты:
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&ДатаНач, ДЕНЬ, 20 * t1.a + t2.b) КАК n
ПОМЕСТИТЬ ВТ_ВсеДаты
ИЗ
(ВЫБРАТЬ 0 КАК a ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2) КАК t1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
(ВЫБРАТЬ 0 КАК b ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9
ОБЪЕДИНИТЬ ВЫБРАТЬ 10 ОБЪЕДИНИТЬ ВЫБРАТЬ 11 ОБЪЕДИНИТЬ ВЫБРАТЬ 12
ОБЪЕДИНИТЬ ВЫБРАТЬ 13 ОБЪЕДИНИТЬ ВЫБРАТЬ 14 ОБЪЕДИНИТЬ ВЫБРАТЬ 15
ОБЪЕДИНИТЬ ВЫБРАТЬ 16 ОБЪЕДИНИТЬ ВЫБРАТЬ 17 ОБЪЕДИНИТЬ ВЫБРАТЬ 18
ОБЪЕДИНИТЬ ВЫБРАТЬ 19) КАК t2
ПО (ИСТИНА)
ГДЕ
ДОБАВИТЬКДАТЕ(&ДатаНач, ДЕНЬ, 20 * t1.a + t2.b) <= &ДатаКон
В данном примере мы создаем комбинацию из двух виртуальных таблиц. Это позволяет получить до 60 последовательных дат (20 * 3). Полученную таблицу ВТ_ВсеДаты мы можем соединить ЛЕВЫМ СОЕДИНЕНИЕМ с нашими данными по сотрудникам. Важно помнить: чтобы не потерять дни для каждого сотрудника, сначала нужно получить таблицу всех сотрудников, соединить её «По ИСТИНА» с таблицей дат (создав сетку), и уже к этой сетке левым соединением привязывать факты присутствия. При необходимости можно воспользоваться методикой построения выборки диапазона дат в запросе.
Поскольку зачастую разработка ведется в типовых конфигурациях (например, ЗУП 3.0), мы можем упростить себе жизнь, обратившись к готовым данным. В системе уже есть регистры, содержащие даты.
Проанализируем использование регистра ДанныеГрафикаРаботы или ПроизводственныйКалендарь. Это гораздо надежнее, чем генерировать даты вручную:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Календарь.Дата КАК Дата
ИЗ
РегистрСведений.ДанныеГрафикаРаботы КАК Календарь
ГДЕ
Календарь.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
Если в базе ведется учет по валютам, как крайний вариант можно использовать РегистрСведений.КурсыВалют, так как там обычно есть записи на каждый день. Однако лучше использовать специализированные регистры графиков.
Иногда логика отчета настолько сложна, что проще всего передать таблицу дат в СКД программно. Для этого в схеме компоновки создается «Набор данных - объект» с именем ТаблицаДат.
Выясним, как передать эти данные в модуле отчета. В процедуре ПриКомпоновкеРезультата мы можем создать таблицу значений, заполнить её циклом и передать в процессор компоновки:
ТаблицаДат = Новый ТаблицаЗначений;
ТаблицаДат.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ТекущаяДата = ДатаНачала;
Пока ТекущаяДата <= ДатаОкончания Цикл
НоваяСтрока = ТаблицаДат.Добавить();
НоваяСтрока.Дата = ТекущаяДата;
ТекущаяДата = ТекущаяДата + 86400; // Добавляем сутки
КонецЦикла;
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ТаблицаДат", ТаблицаДат);
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
Затем в схеме СКД мы связываем этот внешний набор данных с основным запросом через «Связи наборов данных». В качестве условия связи указываем Дата = ДатаОтметки. Обязательно проверьте, чтобы связь была левой (основной набор — таблица дат), иначе пустые дни снова пропадут.
Для решения задачи вывода пустых дней в СКД мы проанализировали четыре подхода. Выбор конкретного метода зависит от вашей ситуации:
Важный совет: При использовании левого соединения для вывода пустых дат всегда используйте функцию ЕСТЬNULL(ПолеСДанными, 0) или ВЫБОР КОГДА Поле ЕСТЬ NULL ТОГДА "" ИНАЧЕ ... в выражениях полей СКД, чтобы отчет выглядел аккуратно и не содержал пустых ячеек с системными значениями NULL.