我有预订'具有以下字段的表
ReservationID int
ReservationDateFrom datetime
ReservationDateTo datetime
ReservationRoom int
我想创建一个sp,它可以选择可用的房间以及给定日期和房间的可用时间。这是我到目前为止所做的工作,但它并没有起作用。如果在给定日期没有房间时间表,并且只返回2个日期时间范围之间的可用房间时间表,则它不会返回任何内容。
CREATE PROCEDURE [dbo].[sp_GetAvailableSchedules](
@date date,
@room int
)
AS
BEGIN
select ReservationID, ReservationDateFrom, ReservationDateTo, ReservationRoom
from Reservation r
where ReservationRoom = @room and @date = CONVERT(date, ReservationDateFrom)
union all
select NULL, ReservationDateTo,
lead(ReservationDateFrom) over (partition by ReservationRoom order by ReservationDateFrom),
ReservationRoom
from reservation r
where ReservationRoom = @room and @date = CONVERT(date, ReservationDateFrom)
END
示例数据:
ReservationID ReservationDateFrom ReservationDateTo ReservationRoom
1 2017-01-02 00:00:00.000 2017-01-02 02:00:00.000 14
2 2017-01-02 04:00:00.000 2017-01-02 05:00:00.000 14
3 2017-01-02 06:00:00.000 2017-01-02 08:00:00.000 14
4 2017-01-02 08:30:00.000 2017-01-02 09:30:00.000 14
5 2017-01-02 09:50:00.000 2017-01-02 11:00:00.000 14
6 2017-01-02 13:00:00.000 2017-01-02 15:00:00.000 14
执行时的预期输出
EXEC sp_GetAvailableSchedules '2017-01-02', 14
TimeIn TimeOut Minutes
02:00 04:00 120
05:00 06:00 60
08:00 08:30 30
09:30 09:50 20
11:00 13:00 120
15:00 24:00 540
下面的sql server 2012的解决方案可以,但如果它可以在至少sql server 2008上运行会更好。
进度更新
我已经尝试将@ zerox981的答案整合到我的sp中,所以
create PROCEDURE [dbo].[sp_GetAvailableSchedules](
@date datetime,
@room int
)
AS
BEGIN
with data as
(
SELECT *,
ISNULL(
(select top 1 ReservationDateFrom from Reservation where ReservationRoom =a.ReservationRoom and ReservationDateFrom> a.ReservationDateTo order by ReservationDateFrom)
, @date+1) as next
from Reservation a
where ReservationRoom = @room
)
select
cast(Reservationdateto as time) TimeIn,
cast(next as time) [TimeOut],
datediff(mi,ReservationDateTo, next) [Minutes]
from data
END
如果这是我插入的数据,我发现了很多问题
select * from Reservation
ReservationID ReservationDateFrom ReservationDateTo ReservationRoom
34 2017-02-17 13:00:00.000 2017-02-17 15:00:00.000 6003
35 2017-02-17 09:00:00.000 2017-02-17 12:00:00.000 6003
36 2017-02-18 12:00:00.000 2017-02-18 14:00:00.000 6003
案例1 - 当日期和房间内有许多预订时
declare @date datetime = '2017-02-17 00:00:00.000' , @room int = 6003
exec [sp_GetAvailableSchedules] @date, @room
预期结果
TimeIn TimeOut Minutes
00:00 09:00 540
12:00 13:00 60
15:00 24:00 540
实际结果
TimeIn TimeOut Minutes
15:00:00.0000000 12:00:00.0000000 1260
12:00:00.0000000 13:00:00.0000000 60
14:00:00.0000000 00:00:00.0000000 -840
案例2 - 当日期和房间内有1次预订时
declare @date datetime = '2017-02-18 00:00:00.000' , @room int = 6003
exec [sp_GetAvailableSchedules] @date, @room
预期结果
TimeIn TimeOut Minutes
00:00 12:00 720
14:00 24:00 600
实际结果
TimeIn TimeOut Minutes
15:00:00.0000000 12:00:00.0000000 1260
12:00:00.0000000 13:00:00.0000000 60
14:00:00.0000000 00:00:00.0000000 600
案例3 - 如果在指定日期和房间内没有预订(全天应该可用)
declare @date datetime = '2017-02-20 00:00:00.000' , @room int = 500
exec [sp_GetAvailableSchedules] @date, @room
预期结果(全天可用)
TimeIn TimeOut Minutes
00:00 24:00 1440
实际结果为空
答案 0 :(得分:3)
我认为您在测试数据或预期输出中存在错误。边框也没有定义等。
这是你可以从
开始的if object_id('tempdb..#r') is not null
drop table #r
create table #r ( ReservationID int , ReservationDateFrom datetime , ReservationDateTo datetime, ReservationRoom int)
--insert into #r values(1, '2017-01-02 00:00:00.000' ,'2017-01-02 02:00:00.000' , 14 )
insert into #r values(2, '2017-01-02 04:00:00.000' ,'2017-01-02 05:00:00.000' , 14 )
insert into #r values(3, '2017-01-02 06:00:00.000' ,'2017-01-02 08:00:00.000' , 14 )
insert into #r values(4, '2017-01-02 08:30:00.000' ,'2017-01-02 09:30:00.000' , 14 )
insert into #r values(5, '2017-01-02 09:50:00.000' ,'2017-01-02 11:00:00.000' , 14 )
insert into #r values(6, '2017-01-02 13:00:00.000' ,'2017-01-02 15:00:00.000' , 14 )
declare @dt datetime ='2017/01/02', @room int = 14
; with data as
(
select ReservationDateFrom, ReservationDateTo,ReservationRoom
from #r
where ReservationRoom = @room
and ReservationDateFrom between @dt and @dt+1
union
select @dt,@dt,@room
union
select @dt+1,@dt+1,@room
)
,mid as
(
select *,
(select top 1 ReservationDateFrom
from data
where ReservationRoom =a.ReservationRoom
and ReservationDateFrom> a.ReservationDateTo
order by ReservationDateFrom) [next]
from data a
)
select
cast(Reservationdateto as time) TimeIn,
cast(next as time) [TimeOut],
datediff(mi,ReservationDateTo, next) [Minutes]
from mid
where datediff(mi,ReservationDateTo, next)>0
您可以在此处进行测试:http://rextester.com/IZH14294
答案 1 :(得分:1)
假设没有重叠,并且分钟是最小的时间单位,当然还有Numbers表,对于SQL Server 2012,这应该有效:
declare @date datetime = '20170102', @room int = 14
;with x as (
select num.n, isnull(lag(num.n) over(order by num.n),-1) n_prev
from utility.numbers num
left join Reservation t on num.n between datediff(minute, @date, reservationdatefrom) and datediff(minute, @date, reservationdateto) and t.reservationroom = @room
where num.n < 1440 and num.n > 0
and t.reservationid is null
)
select *, cast(dateadd(minute, n-1, @date) as time), cast(dateadd (minute, isnull(lead(n_prev+1) over(order by n), 1440), @date) as time),
isnull(lead(n_prev+1) over(order by n), 1440) - (n-1)
from x
where n <> n_prev+1
(2008年的解决方案将包括自我加入。非常丑陋的IMO)。
有关Numbers表的更多信息,请点击此处:
https://dba.stackexchange.com/questions/11506/why-are-numbers-tables-invaluable
答案 2 :(得分:0)
我在表格中添加了一些示例数据并修复了日期。这不是一个复杂的解决方案,但希望它能为您提供有关如何获得所需结果的一些想法。
您可以使用循环或公用表表达式管理结果。 我使用了一个cte和一个声明的表。您可以在我的解决方案中删除声明的表,并将递归部分加入到与基本查询类似的select中,如果需要的话。
基本思路是将预约的结束时间和下一个的开始时间放在同一行。
我只做了选择部分,我会让它成为你的程序。
CREATE TABLE reservation (
ReservationID int,
ReservationDateFrom datetime,
ReservationDateTo datetime,
ReservationRoom int
)
INSERT INTO reservation
(
ReservationID, ReservationDateFrom, ReservationDateTo, ReservationRoom
)
VALUES
(1, '2017-01-02 00:00:00.000', '2017-01-02 02:00:00.000', 14),
(2, '2017-01-02 04:00:00.000', '2017-01-02 05:00:00.000', 14),
(3, '2017-01-02 06:00:00.000', '2017-01-02 08:00:00.000', 14),
(4, '2017-01-02 08:30:00.000', '2017-01-02 09:30:00.000', 14),
(5, '2017-01-02 09:50:00.000', '2017-01-02 11:00:00.000', 14),
(6, '2017-01-02 13:00:00.000', '2017-01-03 15:00:00.000', 14),
(6, '2017-01-03 16:00:00.000', '2017-01-03 17:00:00.000', 14),
(7, '2017-01-04 13:00:00.000', '2017-01-03 15:00:00.000', 14),
(8, '2017-01-02 13:00:00.000', '2017-01-03 15:00:00.000', 15)
DECLARE @date date = '2017-01-02';
DECLARE @room int = 14;
DECLARE @res_table TABLE
(
RowNumber int,
IsReserved bit,
ReservationDateFrom datetime,
ReservationDateTo datetime
);
INSERT INTO @res_table
SELECT
ROW_NUMBER() OVER(PARTITION BY ReservationRoom ORDER BY ReservationDateFrom) as RowNumber,
1 as IsReserved,
ReservationDateFrom,
ReservationDateTo
FROM reservation
WHERE @date BETWEEN CAST(ReservationDateFrom as date) AND CAST(ReservationDateTo as date)
AND @room = ReservationRoom;
WITH cte AS
(
SELECT
RowNumber,
IsReserved,
ReservationDateFrom,
ReservationDateTo
FROM @res_table
UNION ALL
SELECT
cte.RowNumber * -1 as RowNumber,
cast(0 as bit) as IsReserved,
cte.ReservationDateTo as ReservationDateFrom,
rt.ReservationDateFrom as ReservationDateTo
FROM cte
INNER JOIN @res_table rt ON
rt.RowNumber = cte.RowNumber + 1
)
SELECT
cte.ReservationDateFrom as FreeFromDate,
cte.ReservationDateTo as FreeToDate,
DATEDIFF(mi, cte.ReservationDateFrom, cte.ReservationDateTo) as FreeMinutes
FROM cte
WHERE IsReserved = 0
ORDER BY FreeFromDate