繁体   English   中英

在sql server查询(存储过程)中按月分组日期

[英]Grouping dates by month in an sql server query (stored procedure)

我对这个有一点精神障碍。

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

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

问题

我喜欢将其自动化为存储过程。

是否有可能将这个分组为特定年份的几个月?

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

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

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

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

您可以将日期“舍入”到该月的第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]

您可以使用DATEPART功能获取月号和组号。

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

至于跨越几个月,您将必须决定如何对其进行建模。 如果您预订了3个月,该怎么办? 4? 12? 更多?

至于月份,您可以尝试像200911这样的6位数值,这样您就可以轻松地对它们进行排序,但将它们保存在整数字段中。 如果计算了该值,则没有人能够对其进行处理。

尝试:

 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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM