【问题标题】:How to find maximum capacity available in overlapping time periods SQL Server 2008如何查找重叠时间段内可用的最大容量 SQL Server 2008
【发布时间】:2012-03-18 20:22:43
【问题描述】:

我们正在开发一个房间预订系统,同一房间可以被不同的人预订,只要不超过容量。

我们正在尝试计算新预订请求的整个时间段内的最大可用座位数。

这可能最好用图表来解释。假设这个房间有 100 个:

<-------I want to book this - How many seats are available?------->

   <---Booking 1: 10 seats--->       <---Booking 2: 10 seats--->
                       <---Booking 3: 10 seats--->
                                     <---Booking 4: 10 seats--->

因此,在请求期间的任何时候,可用的最大值中的最小值为 70(预订 2、3 和 4 重叠,此时 30 个席位无法使用)。

如何使用 SQL 计算?我们希望查询返回当前可用于请求的时间段的座位数,在上述情况下为 70。

可用的表是 RoomBooking(房间、开始、结束、容量)和房间(id、容量)。

我们使用 SQL Server 2008。

【问题讨论】:

  • 时间段预订的粒度是多少 - 1 分钟、5 分钟、1 小时...?
  • 可以即时预订。比较可以忽略一个预订的结束与另一个预订的开始完全相同......

标签: sql sql-server-2008 tsql


【解决方案1】:

对于从@start_time 到@end_time 的特定@room 和时间段,请尝试:

;with cte as 
(select id, capacity, @start_time time_point
 from room where id = @room
 union all
 select id, capacity, dateadd(mi, 1, time_point) time_point
 from cte where dateadd(mi, 1, time_point) < @end_time)
select min(capacity_left) max_available from
(select c.time_point, max(c.capacity) - sum(b.capacity_taken) capacity_left
 from cte c
 join RoomBooking b
   on c.id = b.room and
      c.time_point >= b.start and
      c.time_point < b.end
 group by c.time_point) sq

请注意,在整个整个预订期间可用的最大容量是该期间内任何时间点的最小可用容量 - 因此在示例中这将是70 个座位。

【讨论】:

  • 非常好——不过要小心,如果Room 没有RoomBooking,您将不会得到任何结果。可以用左外连接修复。在sum(b.capacity_taken) 上,您需要ISNULL - 检查。
  • P.s.:您可能还想包含OPTION (MAXRECURSION 0)
  • 所以本质上,这相当于循环遍历每个time_point 并跟踪最低可用性!
【解决方案2】:

应该有一种方法来唯一标识每个预订。我建议在 RoomBooking 表中添加一个标识列并解决如下代码中的问题:

create table Room
(
    id int identity(1, 1) primary key clustered,
    capacity int not null
)
go
create table RoomBooking
(
    id int identity(1, 1) primary key clustered,
    room int constraint FK_RoomBooking_Room foreign key references Room(id),
    start_time datetime,
    end_time datetime,
    capacity_taken int
)
go

insert Room(capacity)
    select 100 union all
    select 200

insert RoomBooking(room, start_time, end_time, capacity_taken)
    select 1, '2012-02-29 10:00', '2012-02-29 12:00', 10 union all
    select 1, '2012-02-29 11:00', '2012-02-29 15:00', 10 union all
    select 1, '2012-02-29 14:00', '2012-02-29 16:00', 10 union all
    select 2, '2012-02-29 14:00', '2012-02-29 16:00', 10 union all
    select 2, '2012-02-29 14:00', '2012-02-29 16:00', 10 union all
    select 2, '2012-02-29 14:00', '2012-02-29 16:00', 10 union all
    select 2, '2012-02-29 13:00', '2012-02-29 15:00', 10 union all
    select 2, '2012-02-29 15:00', '2012-02-29 17:00', 10 union all
    select 2, '2012-02-29 17:00', '2012-02-29 19:00', 10 union all
    select 1, '2012-02-29 14:00', '2012-02-29 16:00', 10

go

declare @roomid int = 1
declare @check_period_start datetime = '2012-02-29 13:00'
declare @check_period_end datetime = '2012-02-29 15:00'

select
    r.id, r.capacity - maxtaken.max_capacity_taken as remaining_capacity
from
    Room r
    join
        (
            select
                id, MAX(sum_capacity_taken) max_capacity_taken
            from
                (
                    select
                        r.id, SUM(rb2.capacity_taken) + min(rb1.capacity_taken) sum_capacity_taken
                    from
                        Room r
                        join RoomBooking rb1 on rb1.room = r.id
                        left join RoomBooking rb2
                            on rb1.room = rb2.room
                               and rb1.id <> rb2.id
                               and 
                               (
                                   (rb2.start_time <= rb1.start_time and rb2.end_time >= rb1.start_time)
                                   or (rb2.start_time <= rb1.end_time and rb2.end_time >= rb1.end_time)
                               )
                        where
                            rb1.end_time >= @check_period_start
                            and rb1.start_time <= @check_period_end
                            and rb2.end_time >= @check_period_start
                            and rb2.start_time <= @check_period_end
                        group by
                            r.id, rb1.id
                ) sct
                group by id
        ) maxtaken on maxtaken.id = r.id
where
    r.id = @roomid

【讨论】:

  • 感谢您的回答。我们确实有一个房间预订 ID,我只是忘了提。
【解决方案3】:

这是一种方法。我首先使用名为“小时”的递归 CT 创建了一个影响约会的小时列表。然后对于每个小时,CTE“hour_capacity”将预订的座位数量相加。最终查询计算每小时未使用的座位数。

; with  hours as
        (
        select  @startdt as dt
        union all
        select  dateadd(hour, 1, dt)
        from    Hours
        where   dateadd(hour, 1, dt) < @enddt
        )
,       hour_capacity as
        (
        select  h.dt
        ,       r.capacity
        ,       sum(b.seats) as seats_used
        from    @room r
        cross join
                hours h
        join    @booking b
        on      b.roomid = r.id
                and h.dt between b.startdt and b.enddt
        where   r.id = @roomid
        group by
                h.dt
        ,       r.capacity
        )
select  capacity - max(seats_used)
from    hour_capacity
group by
        capacity

Full example at SE Data.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-04
    • 2021-04-21
    • 2016-04-29
    相关资源
    最近更新 更多