简体   繁体   English

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

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

Im having a bit of a mental block on this one. 我对这个有一点精神障碍。

I got booking system for hotel rooms and it contains a table as so 我有酒店房间的预订系统,它包含一张桌子

BookingRoomLink BookingRoomLink
BookingId (FK) BookingId(FK)
RoomId (FK) RoomId(FK)
Start_Date 开始日期
End_Date 结束日期

I'd like to query the data to extract occupancy levels for each month. 我想查询数据以提取每个月的入住率。 I could do this manually (ie for the past month do something like this). 我可以手动完成这个(即过去一个月做这样的事情)。

 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()

Then i can do a count on the results or similar which would give me the room nights "used" and subtract this against the room nights available in a month. 然后我可以对结果或类似情况进行计数,这会给我房间的夜晚“使用”,并在一个月内的房间晚上减去这个。

Eg. 例如。 10 rooms x 30 days in the month = 300 possible room nights available. 10个房间x每月30天= 300个可能的房晚。 150 used (result from query) = 50% occupancy. 使用了150(来自查询的结果)= 50%的占用率。

The problem 问题

Id like to automate this into a stored procedure. 我喜欢将其自动化为存储过程。

Is it possible to group this into months for a given year? 是否有可能将这个分组为特定年份的几个月?

How would I ensure that bookings which overlap a month boundry are suitable handled? 我如何确保适合处理一个月边界的预订?

If you need to do this often, you could add those month and year parts as persisted computed columns to your table, and put an index on them: 如果您经常需要这样做,可以将这些月份和年份部分作为持久计算列添加到表中,并为它们添加索引:

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

You could now select these new computed columns, use them in a WHERE clause, GROUP by those columns - and they'll always be up to date based on Start_Date and End_Date - they're not computed everytime you access them --> much faster than just using DATEPART in all your queries! 您现在可以选择这些新的计算列,在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

You could "Round" the date to the 1st of the month, and then GROUP BY on that. 您可以将日期“舍入”到该月的第1天,然后再进行GROUP BY。 Similar to using DatePart, but you still have a valid date, so you can use a Date Range in the WHERE clause before or after doing the Grouping. 与使用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功能获取月号和组号。

If You can add a computed column to your table, make its value the month you want to use. 如果您可以向表中添加计算列,请将其值设置为您要使用的月份。 I would definitely choose "store with row" vs. compute each time. 我肯定会选择“与行存储”而不是每次计算。

As for spanning months, you'll have to decide how you want to model that. 至于跨越几个月,您将必须决定如何对其进行建模。 What if you have a reservation that spans 3 months? 如果您预订了3个月,该怎么办? 4? 4? 12? 12? More? 更多?

As for the month, you might try a 6-digit value like 200911 so you can easily sort them but keep them in an integer field. 至于月份,您可以尝试像200911这样的6位数值,这样您就可以轻松地对它们进行排序,但将它们保存在整数字段中。 If the value is computed, no one will be able to doink with it. 如果计算了该值,则没有人能够对其进行处理。

Try: 尝试:

 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