如何查找重叠时间段SQL Server 2008中可用的最大容量

时间:2012-02-29 10:42:51

标签: sql sql-server-2008 tsql

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

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

最好用图表解释。假设这个房间容纳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(房间,开始,结束,capacity_taken)和房间(id,容量)。

我们使用SQL Server 2008。

3 个答案:

答案 0 :(得分:4)

对于从@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个座位。

答案 1 :(得分:1)

应该有一种方法来唯一识别每个预订。我建议在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

答案 2 :(得分:0)

这是一种方法。我首先创建一个使用名为“hours”的递归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.