2

我对这个有点心理障碍。

我有酒店房间的预订系统,它包含一张桌子

BookingRoomLink
BookingId (FK)
RoomId (FK)
Start_Date
End_Date

我想查询数据以提取每个月的入住率。我可以手动执行此操作(即在过去的一个月中执行此类操作)。

 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% 的占用率。

问题

我想把它自动化成一个存储过程。

是否可以将其分组为给定年份的月份?

我将如何确保适合处理与一个月边界重叠的预订?

4

6 回答 6

3

如果您需要经常这样做,您可以将这些月份和年份部分作为持久计算列添加到您的表中,并在它们上放置一个索引:

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在您的所有查询中使用!

于 2009-11-16T18:19:16.447 回答
3
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
于 2009-11-16T18:19:53.217 回答
2

您可以将日期“四舍五入”到该月的 1 日,然后在该日期上进行 GROUP BY。与使用 DatePart 类似,但您仍然有一个有效日期,因此您可以在进行分组之前或之后在 WHERE 子句中使用日期范围。

SELECT [Date] = DATEADD(Month, DATEDIFF(Month, 0, Start_Date), 0), -- 每月 1 日
       [预订] = COUNT(*)
来自 BookingRoomLink
GROUP BY DATEADD(月, DATEDIFF(月, 0, Start_Date), 0)
订购日期 [日期]
于 2009-11-16T18:33:35.887 回答
0

您可以使用DATEPART函数获取月份编号并按该编号分组。

于 2009-11-16T18:11:59.597 回答
0

如果您可以将计算列添加到表中,请将其值设为您要使用的月份。我肯定会选择“与行一起存储”与每次计算。

至于跨越几个月,您必须决定如何建模。如果您的预订时间跨度为 3 个月,该怎么办?4?12?更多的?

至于月份,您可以尝试使用 6 位数的值,例如 200911,这样您就可以轻松地对它们进行排序,但将它们保存在一个整数字段中。如果该值是计算出来的,那么没有人可以使用它。

于 2009-11-16T18:16:18.590 回答
0

尝试:

 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)
于 2009-11-16T18:32:36.060 回答