При разработке отчетов, связанных с анализом активности (например, запись звонков, работа сотрудников или загрузка оборудования), часто возникает задача визуализации данных в виде временной сетки. Стандартные функции языка запросов позволяют легко получить начало часа или дня, но когда требуется разбить день на произвольные отрезки, например, по 10 или 20 минут, разработчики сталкиваются с трудностями. Рассмотрим подробнее несколько эффективных способов решения этой задачи, проанализируем их плюсы и минусы, а также разберем по шагам реализацию каждого метода.
Один из самых простых и гибких способов — заранее подготовить список всех необходимых временных слотов и передать их в запрос. Проанализируем этот подход: мы создаем таблицу значений в коде 1С, заполняем её нужными интервалами (08:00, 08:10, 08:20 и так далее), а затем передаем в запрос как параметр.
Разберем пример реализации в программном коде:
ТЗ_Интервалы = Новый ТаблицаЗначений;
ТЗ_Интервалы.Колонки.Добавить("НачалоИнтервала", Новый ОписаниеТипов("Дата"));
ТЗ_Интервалы.Колонки.Добавить("КонецИнтервала", Новый ОписаниеТипов("Дата"));
ТекущееВремя = НачалоДня(ВыбраннаяДата) + 8 * 3600; // Начинаем с 08:00
Пока ТекущееВремя < (НачалоДня(ВыбраннаяДата) + 20 * 3600) Цикл
НоваяСтрока = ТЗ_Интервалы.Добавить();
НоваяСтрока.НачалоИнтервала = ТекущееВремя;
ТекущееВремя = ТекущееВремя + 600; // Добавляем 10 минут
НоваяСтрока.КонецИнтервала = ТекущееВремя;
КонецЦикла;
В самом запросе мы используем ЛЕВОЕ СОЕДИНЕНИЕ этой временной таблицы с нашими данными (например, документами «Звонок»). Условие соединения будет выглядеть так: Данные.Дата >= ВтИнтервалы.НачалоИнтервала И Данные.Дата < ВтИнтервалы.КонецИнтервала. Это гарантирует, что каждый звонок попадет в свой временной слот.
Если мы не хотим передавать внешние данные, можно сгенерировать сетку времени прямо в теле запроса. Для этого используется техника декартова произведения маленьких таблиц с числами. Рассмотрим ситуацию, когда нам нужно получить 144 десятиминутных интервала (сутки).
Сначала создадим временную таблицу Цифры, содержащую значения от 0 до 9. Соединив её саму с собой, мы получим последовательности 0–99, 0–999 и т.д. Эти числа мы будем использовать как смещение в минутах от начала дня.
ВЫБРАТЬ 0 КАК Цифра ПОМЕСТИТЬ ВТ_Цифры ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ... ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9;
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&НачалоДня, МИНУТА, (Т1.Цифра + Т2.Цифра * 10 + Т3.Цифра * 100) * 10) КАК ПериодСетка
ПОМЕСТИТЬ ВТ_Сетка
ИЗ
ВТ_Цифры КАК Т1,
ВТ_Цифры КАК Т2,
ВТ_Цифры КАК Т3
ГДЕ
(Т1.Цифра + Т2.Цифра * 10 + Т3.Цифра * 100) * 10 < 1440
Этот метод позволяет получить чистую таблицу периодов без обращения к метаданным или внешним наборам данных. После формирования ВТ_Сетка мы соединяем её с основной таблицей документов.
Выясним причину, почему стандартная функция НАЧАЛОПЕРИОДА не всегда подходит. Она умеет работать только с фиксированными типами: ЧАС, ДЕНЬ, НЕДЕЛЯ. Если нам нужен интервал в 10 минут, мы можем применить математический трюк внутри запроса. Логика заключается в вычислении количества секунд, прошедших с начала дня, и их «приземлении» на кратную величину.
Рассмотрим формулу для 10-минутного интервала (600 секунд):
1. Находим разность в секундах: РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ДатаСобытия, ДЕНЬ), ДатаСобытия, СЕКУНДА).
2. В системе 1С запросы не поддерживают взятие остатка или полноценное деление с округлением в классическом виде, поэтому для группировки часто используют предварительно созданный вспомогательный регистр или справочник «Тайм-слоты».
Если основной целью является построение отчета, то наиболее правильным решением будет использование Системы компоновки данных (СКД). В СКД существует встроенный механизм дополнения периодов. Проанализируем ситуацию: если в 08:10 звонков не было, обычный запрос просто пропустит эту строку, и в отчете возникнет «дырка».
Чтобы этого избежать:
Дата.Однако для интервала именно в 10 минут стандартного дополнения в СКД нет. В этом случае мы возвращаемся к Способу 1 или Способу 2, создавая «скелет» отчета из всех интервалов, и левым соединением добавляем к нему реальные данные документов.
Как отмечалось в сообщениях форума, если данных много и отчет строится часто, расчет интервалов «на лету» может замедлить систему. В этом случае целесообразно создать Регистр накопления (вид — обороты).
Рассмотрим алгоритм работы с регистром:
Интервал (например, справочник или перечисление со значениями «08:00», «08:10»...).Обороты этого регистра. Это максимально быстрый способ получения данных, так как вся сложная логика расчета интервалов выполняется один раз в момент записи документа, а не каждый раз при формировании отчета.Проанализируем критически важный момент — обработку пограничных значений времени. Если звонок начался ровно в 08:10:00, в какой интервал он должен попасть: в «08:00–08:10» или в «08:10–08:20»? Чтобы избежать дублирования данных или их потери, всегда придерживайтесь правила: включаем начало и исключаем конец.
В запросе это реализуется строгим неравенством для верхней границы:
ГДЕ Документ.Дата >= Интервал.Начало И Документ.Дата < Интервал.Конец
Использование МЕЖДУ в данном случае является ошибкой, так как оно включает обе границы, и событие на стыке минут может задвоиться в отчете.
Таким образом, выбор метода зависит от ваших задач. Для разового простого отчета подойдет Способ 1 с передачей таблицы значений. Для сложной аналитической панели с высокой нагрузкой лучше реализовать Способ 5 с регистрами накопления. А для динамического формирования сетки внутри кода без лишних объектов метаданных идеально подходит Способ 2.