Группировка дат по месяцам в запросе к серверу sql (хранимая процедура) - PullRequest
2 голосов
/ 16 ноября 2009

У меня немного психический блок на этом.

У меня есть система бронирования номеров в отелях, и она содержит таблицу как

BookingRoomLink
BookingId (FK)
RoomId (FK)
Дата_начала
Датой_окончания

Я бы хотел запросить данные для извлечения уровней занятости за каждый месяц. Я мог бы сделать это вручную (то есть за последний месяц сделать что-то подобное).

 SELECT BookingRoomLink.Start_Date,
        BookingRoomLink.End_Date,
        DATEDIFF("d", BookingRoomLink.Start_Date, BookingRoomLink.End_Date) as RoomNights
   FROM BookingRoomLink 
  WHERE BookingRoomLink.Start_Date >= dateadd(m, -1, getdate()) 
    AND BookingRoomLink.End_Date <= GETDATE()

Тогда я могу подсчитать результаты или аналогичные результаты, которые дадут мне «использованные» ночи в комнате, и вычесть их из числа комнат, доступных за месяц.

Например. 10 комнат x 30 дней в месяце = 300 возможных ночей в номере. 150 использованных (результат запроса) = 50% занятости.

Проблема

Я бы хотел автоматизировать это в хранимую процедуру.

Можно ли сгруппировать это по месяцам для данного года?

Как бы я обеспечил надлежащую обработку заказов, которые перекрывают границы месяца?

Ответы [ 6 ]

3 голосов
/ 16 ноября 2009
WITH    (
        SELECT  0
        UNION ALL
        SELECT  m + 1
        FROM    mon
        WHERE   m < 11
        ),
        yr (y) AS
        (
        SELECT  CAST('1990-01-01' AS DATETIME)
        UNION ALL
        SELECT  DATEADD(year, 1, y)
        FROM    yr
        WHERE   y <= GETDATE()
        ),
        dates (smy, emy) AS
        (
        SELECT  DATEADD(month, m, y), DATEADD(month, m + 1, y)
        FROM    yr
        CROSS JOIN
                mon
        ),
        diffs (smy, emy, days) AS
        (
        SELECT  smy, emy, DATEDIFF(day, smy, emy)
        FROM    dates
        )
SELECT  smy,
        roomId,
        CAST(SUM(DATEDIFF(day,
        CASE WHEN start_date < smy THEN smy ELSE start_date END,
        CASE WHEN end_date > emy THEN emy ELSE end_date END
        )) AS FLOAT) / days
FROM    diffs
JOIN    bookings
ON      start_date < emy
        AND end_date >= smy
GROUP BY
        roomId, smy, emy, days
3 голосов
/ 16 ноября 2009

Если вам нужно делать это часто, вы можете добавить эти части месяца и года в виде постоянных вычисляемых столбцов к вашей таблице и поместить в них индекс:

ALTER TABLE dbo.BookingRoomLink 
   ADD StartMonth AS MONTH(Start_Date) PERSISTED

ALTER TABLE dbo.BookingRoomLink 
   ADD StartYear AS Year(Start_Date) PERSISTED

ALTER TABLE dbo.BookingRoomLink 
   ADD EndMonth AS MONTH(End_Date) PERSISTED

ALTER TABLE dbo.BookingRoomLink 
   ADD EndYear AS Year(End_Date) PERSISTED

Теперь вы можете выбрать эти новые вычисляемые столбцы, использовать их в предложении WHERE, GROUP по этим столбцам - и они всегда будут обновляться на основе Start_Date и End_Date - они не вычисляются при каждом обращении к ним - > гораздо быстрее, чем просто использовать DATEPART во всех ваших запросах!

2 голосов
/ 16 ноября 2009

Вы можете «округлить» дату до 1 числа месяца, а затем GROUP BY по этому вопросу. Аналогично использованию DatePart, но у вас все еще есть действительная дата, поэтому вы можете использовать диапазон дат в предложении WHERE до или после группировки.

SELECT [Date] = DATEADD(Month, DATEDIFF(Month, 0, Start_Date), 0),    -- 1st of the month
       [Bookings] = COUNT(*)
FROM   BookingRoomLink
GROUP BY DATEADD(Month, DATEDIFF(Month, 0, Start_Date), 0)
ORDER BY [Date]
0 голосов
/ 16 ноября 2009

Попробуйте:

 Select DateName(month, start_Date) + 
        DateName(Year, Start_Date) as MonthName,
    Sum(DateDiff(Day, Start_Date, 
         Case DateDiff(Month, Start_Date, End_Date)
            When 0 Then End_Date 
            Else DateAdd(day, -day(Start_Date), 
                  DateAdd(day, DateDiff(day, 0, Start_Date), 0)) Bookings
 From BookingRoomLink
 Group By DateName(month, start_Date) + DateName(Year, Start_Date)
0 голосов
/ 16 ноября 2009

Если вы можете добавить вычисляемый столбец в таблицу, укажите его значение в том месяце, который вы хотите использовать. Я бы определенно выбрал «хранить с строкой» против вычислений каждый раз.

Что касается месяцев, вам придется решить, как вы хотите смоделировать это. Что делать, если у вас есть резервирование, которое охватывает 3 месяца? 4? 12? Больше?

Что касается месяца, вы можете попробовать 6-значное значение, например 200911, чтобы вы могли легко отсортировать их, но сохранить их в целочисленном поле. Если значение будет вычислено, никто не сможет с ним связаться.

0 голосов
/ 16 ноября 2009

Вы можете использовать функцию DATEPART , чтобы получить номер месяца и группу по этому номеру.

...