В практике разработки на платформе 1С часто возникает задача анализа занятости ресурсов. Одной из классических вариаций является поиск дня с максимальным количеством одновременно проживающих гостей в отеле. Задача осложняется тем, что данные в базе обычно хранятся в виде интервалов (дата заезда и дата выезда), а нам необходимо получить статистику по конкретным дням, включая те, в которые не происходило никаких движений (заездов или выездов).
В данной статье мы подробно разберем, как реализовать этот алгоритм исключительно средствами запросов, обсудим современные возможности платформы и выясним, как избежать типичных ошибок при расчетах.
Рассмотрим исходные данные: у нас есть набор записей, где для каждого клиента указан день приезда и день отъезда. Наша цель — определить день, в который в отеле находилось максимальное количество людей одновременно. Если таких дней несколько, по условию задачи нам нужно вывести самый первый из них.
Основная идея решения заключается в преобразовании интервалов в «события». Каждое проживание — это два события:
Суммируя эти изменения в хронологическом порядке, мы получаем «нарастающий итог», который и показывает количество проживающих в каждый конкретный момент времени.
Проанализируем самый распространенный метод, который подходит для старых версий платформы и универсален для большинства СУБД. Процесс разделяется на несколько этапов.
Этап 1: Подготовка таблицы событий. Мы объединяем данные о приезде и отъезде в одну временную таблицу.
ВЫБРАТЬ
ТЧ.ДатаПриезда КАК Дата,
1 КАК Изменение
ПОМЕСТИТЬ ВТ_События
ИЗ
Документ.Расписание КАК ТЧ
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ТЧ.ДатаОтъезда,
-1
ИЗ
Документ.Расписание КАК ТЧ
;
Этап 2: Группировка по датам. В один день может быть несколько заездов и выездов, поэтому схлопываем данные.
ВЫБРАТЬ
ВТ.Дата КАК Дата,
СУММА(ВТ.Изменение) КАК СуммаИзменений
ПОМЕСТИТЬ ВТ_ИзмененияПоДням
ИЗ
ВТ_События КАК ВТ
СГРУППИРОВАТЬ ПО
ВТ.Дата
;
Этап 3: Расчет нарастающего итога. Теперь нам нужно для каждой даты посчитать сумму всех изменений, которые произошли до этой даты включительно. Для этого мы соединяем таблицу ВТ_ИзмененияПоДням саму с собой по условию Т1.Дата >= Т2.Дата.
ВЫБРАТЬ ПЕРВЫЕ 1
Т1.Дата КАК ДеньРаботы,
СУММА(Т2.СуммаИзменений) КАК КоличествоГостей
ИЗ
ВТ_ИзмененияПоДням КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ИзмененияПоДням КАК Т2
ПО Т1.Дата >= Т2.Дата
СГРУППИРОВАТЬ ПО
Т1.Дата
УПОРЯДОЧИТЬ ПО
КоличествоГостей УБЫВ,
ДеньРаботы ВОЗР
Этот запрос выведет ровно один день с максимальной загрузкой. Использование УПОРЯДОЧИТЬ ПО КоличествоГостей УБЫВ, ДеньРаботы ВОЗР гарантирует, что при равенстве максимумов мы получим самую раннюю дату.
Если ваша информационная база работает на современной версии платформы, расчет нарастающего итога можно значительно упростить и ускорить. Использование оконных функций избавляет от тяжелого соединения O(n^2), которое при 10 000 записей может выполняться ощутимо долго.
Рассмотрим, как изменится финальная часть запроса с применением конструкции ОБРАБОТКА ИЗМЕНЕНИЙ:
ВЫБРАТЬ
Т.Дата КАК ДеньРаботы,
СУММА(Т.СуммаИзменений) ОБРАБОТКА ИЗМЕНЕНИЙ
ПОРЯДОК ПО Т.Дата КАК КоличествоГостей
ПОМЕСТИТЬ ВТ_Итоги
ИЗ
ВТ_ИзмененияПоДням КАК Т
;
ВЫБРАТЬ ПЕРВЫЕ 1
ВТ.ДеньРаботы,
ВТ.КоличествоГостей
ИЗ
ВТ_Итоги КАК ВТ
УПОРЯДОЧИТЬ ПО
ВТ.КоличествоГостей УБЫВ,
ВТ.ДеньРаботы ВОЗР
Такой подход работает за линейное время и является предпочтительным для высоконагруженных систем.
Проанализируем важный нюанс, упомянутый участниками обсуждения. Что если максимальная загрузка длилась 3 дня, но во второй день никто не заезжал и не уезжал? В нашей таблице ВТ_ИзмененияПоДням этой даты просто не будет, так как там фиксируются только моменты изменений.
Для решения этой проблемы в 1С применяют методику «Календаря» или «Таблицы чисел». Разберем по шагам, как заполнить дыры в периоде:
Это гарантирует, что в анализе будут участвовать абсолютно все дни, и мы не пропустим пиковую дату только потому, что в этот день администратор отеля не регистрировал новых гостей.
Выясним причину возможных расхождений в цифрах. В гостиничном бизнесе критически важно понимать: считается ли день отъезда днем нахождения в отеле? Рассмотрим две ситуации:
Ситуация А: Клиент уезжает 8-го числа вечером. Он занимал место весь день 8-го числа. В этом случае в запросе дата отъезда используется «как есть».
Ситуация Б: Клиент уезжает 8-го числа в 10:00, а в 14:00 на его место заезжает другой. Если мы просто прибавим +1 (заезд) и -1 (отъезд), то в отчете на 8-е число у нас получится корректное сальдо. Но если мы хотим считать именно «ночевки», то событие выезда часто смещают или корректируют логику запроса так, чтобы выехавший утром не суммировался с заехавшим днем.
Для корректного решения в рамках задачи «наибольшее количество клиентов за день» обычно принимается, что клиент был в отеле и в день приезда, и в день отъезда. Если же логика иная, то в запросе к дате отъезда следует прибавлять 1 день, чтобы «минусовать» остаток только со следующего дня после выезда.
Чтобы программа работала быстро даже при большом объеме данных (сотни тысяч записей), придерживайтесь следующих правил:
ВТ_ИзмененияПоДням обязательно нужно проиндексировать по полю Дата перед выполнением соединения или оконной функции.OVER (ORDER BY...)), которые в 1С имеют свои особенности написания.Таким образом, мы рассмотрели комплексный подход к поиску пиковых значений. Сочетание правильной подготовки данных (событийной модели) и современных инструментов платформы 1С позволяет решать такие задачи эффективно и элегантно.