Как в 1С сгруппировать данные по часам и вывести количество чеков в запросе

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

При анализе эффективности работы торговой точки часто возникает задача распределения нагрузки по часам. Например, необходимо понять, в какие периоды времени (с 8:00 до 9:00, с 9:00 до 10:00 и так далее) пробивается наибольшее количество чеков. В этой статье мы разберем, как реализовать такую задачу с помощью механизмов платформы 1С:Предприятие 8, используя язык запросов и программную обработку результатов.

Постановка задачи и базовый запрос

Представим ситуацию: у нас есть внешняя база данных (через ВнешнийИсточникДанных) или внутренняя таблица с документами, где хранится время закрытия чека в формате Дата (например, 01.01.2020 12:34:56). Нам нужно получить таблицу, в которой будет две колонки: Период (текстовое или временное представление часа) и Количество (число закрытых чеков за этот час).

Рассмотрим исходный запрос, который выбирает просто список всех времен закрытия:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    sms_Cheque.CloseTime КАК Время
|ИЗ
|    ВнешнийИсточникДанных.SIS.Таблица.sms_Cheque КАК sms_Cheque
|ГДЕ
|    sms_Cheque.CloseTime МЕЖДУ &ДатаНач И &ДатаКон";

Этот запрос выдаст нам сырые данные, которые неудобно анализировать. Чтобы сгруппировать их по часам, нам потребуется функция НАЧАЛОПЕРИОДА.

Решение через группировку в запросе

Самый эффективный способ решить задачу — переложить расчеты на сторону СУБД. Для этого мы используем функцию НАЧАЛОПЕРИОДА(Дата, ЧАС). Она "отсекает" минуты и секунды, превращая любое время внутри часа в начало этого часа. Например, 12:34:56 станет 12:00:00.

Проанализируем правильную структуру запроса:


ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(sms_Cheque.CloseTime, ЧАС) КАК ЧасЗакрытия,
    КОЛИЧЕСТВО(*) КАК КоличествоЧеков
ИЗ
    ВнешнийИсточникДанных.SIS.Таблица.sms_Cheque КАК sms_Cheque
ГДЕ
    sms_Cheque.CloseTime МЕЖДУ &ДатаНач И &ДатаКон
СГРУППИРОВАТЬ ПО
    НАЧАЛОПЕРИОДА(sms_Cheque.CloseTime, ЧАС)
УПОРЯДОЧИТЬ ПО
    ЧасЗакрытия

Почему это работает:

  1. Функция НАЧАЛОПЕРИОДА с параметром ЧАС позволяет объединить все записи, относящиеся к одному часу, в одну группу.
  2. Агрегатная функция КОЛИЧЕСТВО(*) подсчитывает число записей в каждой сформированной группе.
  3. Секция СГРУППИРОВАТЬ ПО обязательна, так как мы используем агрегатные функции вместе с обычными полями.

Обработка проблемы «пустых часов»

Вышеуказанный запрос имеет одну особенность: если в период с 14:00 до 15:00 не было ни одного чека, эта строка вообще не попадет в результат запроса. Однако для отчета пользователю часто важно видеть все часы (с 8 до 23), даже если там стоят нули.

Рассмотрим, как заполнить недостающие интервалы программно в ТаблицеЗначений. Сначала создадим эталонную таблицу со всеми нужными нам часами:


// Создаем таблицу для результата
ТабРезультат = Новый ТаблицаЗначений;
ТабРезультат.Колонки.Добавить("Период", Новый ОписаниеТипов("Строка"));
ТабРезультат.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));

// Заполняем её шаблонами часов с 8 до 23
Для ч = 8 По 23 Цикл
    НоваяСтр = ТабРезультат.Добавить();
    НоваяСтр.Период = "с " + ч + " до " + (ч + 1);
    НоваяСтр.Количество = 0;
КонецЦикла;

// Выполняем запрос
Результат = Запрос.Выполнить().Выбрать();

// Обходим результат запроса и обновляем данные в нашей таблице
Пока Результат.Следующий() Цикл
    ТекущийЧас = Час(Результат.ЧасЗакрытия);
    Если ТекущийЧас >= 8 И ТекущийЧас <= 23 Тогда
        // Находим нужную строку в эталонной таблице
        // (Индекс строки будет ТекущийЧас - 8, так как начали с 8)
        ТабРезультат[ТекущийЧас - 8].Количество = Результат.КоличествоЧеков;
    КонецЕсли;
КонецЦикла;

Нюансы работы с внешними источниками данных

При работе с ВнешнийИсточникДанных важно помнить, что 1С пытается транслировать функции языка запросов (такие как НАЧАЛОПЕРИОДА) в синтаксис конкретной СУБД (SQL Server, MySQL и др.). В некоторых случаях сложные функции могут не поддерживаться драйвером.

Если НАЧАЛОПЕРИОДА выдает ошибку, проанализируем альтернативный вариант — использование функции ЧАС(Дата):


ВЫБРАТЬ
    ЧАС(sms_Cheque.CloseTime) КАК ТолькоЧас,
    КОЛИЧЕСТВО(*) КАК КоличествоЧеков
ИЗ
    ВнешнийИсточникДанных.SIS.Таблица.sms_Cheque КАК sms_Cheque
ГДЕ
    sms_Cheque.CloseTime МЕЖДУ &ДатаНач И &ДатаКон
СГРУППИРОВАТЬ ПО
    ЧАС(sms_Cheque.CloseTime)

Функция ЧАС вернет просто число от 0 до 23. Это значение еще проще использовать для поиска нужной строки в ТаблицаЗначений или для формирования красивого текстового представления.

Профессиональный подход: использование СКД

Если задача стоит шире, чем просто вывод в таблицу на форме, лучше использовать Систему компоновки данных (СКД). Выясним, какие преимущества это дает:

  1. Дополнение периодов: В настройках группировки по полю ЧасЗакрытия можно включить флаг "Дополнять периоды". Система сама добавит строки с нулевыми значениями для отсутствующих часов без написания кода на языке 1С.
  2. Форматирование: Через свойства поля в СКД можно задать формат вывода времени, чтобы вместо "01.01.2020 08:00:00" выводилось просто "08:00".
  3. Гибкость: Пользователь сможет самостоятельно менять интервалы (например, группировать по 15 минут или по дням) без изменения программного кода.

Оптимизация производительности

Выполним оценку производительности: если в таблице чеков миллионы записей, группировка по вычисляемому полю (функции НАЧАЛОПЕРИОДА или ЧАС) может работать медленно, так как СУБД придется вычислять значение для каждой строки.

Если этот отчет планируется формировать часто, рекомендуется:

Таким образом, мы рассмотрели несколько способов решения задачи: от простого запроса с группировкой до создания гибкого отчета с заполнением пустых интервалов. Выбор конкретного метода зависит от требований к визуализации и объема данных в вашей системе.

← На главную