【问题标题】:Grouping dates by month in an sql server query (stored procedure)在 sql server 查询中按月分组日期(存储过程)
【发布时间】:2009-11-16 18:06:53
【问题描述】:

我对这个有点心理障碍。

我有酒店房间的预订系统,它包含一个表格

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

问题

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

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

我将如何确保适合处理与月份范围重叠的预订?

【问题讨论】:

  • 样本数据和预期结果总是有帮助的!

标签: sql sql-server sql-server-2008 date-manipulation


【解决方案1】:
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 用于使用简单的解决方案,并考虑到在 1 号之前开始或在当月最后一天之后结束的停留
  • sql 2008 为我提供了此 Msg 155、Level 15、State 1、Line 19 'mon' 不是公认的 dateadd 选项。消息 174,级别 15,状态 1,第 30 行 datediff 函数需要 3 个参数。
【解决方案2】:

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

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 快得多!

【讨论】:

    【解决方案3】:

    您可以将日期“四舍五入”到该月的 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) 订购日期 [日期]

    【讨论】:

    • 我喜欢这个解决方案。简单的!我喜欢少打字。谢谢。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

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

      至于月份,您可以尝试使用 6 位数的值,例如 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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-06
          • 1970-01-01
          • 1970-01-01
          • 2022-12-13
          相关资源
          最近更新 更多