我知道这个可能会让人感到有些困惑,我只是想着考虑最好的方法来解决它!我现在在几个论坛上发布了这个,但我似乎没有任何运气。希望有人可以就如何做到这一点提出一些建议。
示例表(tbl_Bookings)
ID DateStarted DateEnded RoomID
1 16/07/2012 09:00 16/07/2012 10:00 1
2 16/07/2012 12:00 16/07/2012 13:00 1
基本上,我想输入2个日期时间,例如16/07/2012 08:30和16/07/2012 13:30,它将查询我上面的示例表并返回“可用”时间,IE,我希望它输出以下内容。
16/07/2012 08:30 - 16/07/2012 09:00
16/07/2012 10:00 - 16/07/2012 12:00
16/07/2012 13:00 - 16/07/2012 13:30
我的问题是,这完全可以在SQL中使用吗?我试着想一想如何在VB中做到这一点,我也在努力解决这个问题。我的想法/尝试一直在使用Ron Savage的fn_daterange(如下所示)
if exists (select * from dbo.sysobjects where name = 'fn_daterange') drop function fn_daterange;
go
create function fn_daterange
(
@MinDate as datetime,
@MaxDate as datetime,
@intval as datetime
)
returns table
as
return
WITH times (startdate, enddate, intervl) AS
(
SELECT @MinDate as startdate, @MinDate + @intval - .0000001 as enddate, @intval as intervl
UNION ALL
SELECT startdate + intervl as startdate, enddate + intervl as enddate, intervl as intervl
FROM times
WHERE startdate + intervl <= @MaxDate
)
select startdate, enddate from times;
go
然后我会用下面的方法来调用它,但我的问题是,我.DateEnded不在dr.enddate中,因此出现的次数为'0':
SELECT dr.startdate, dr.enddate, count(me.DateStarted) as occurrence
FROM fn_daterange('16/07/2012 08:30', '16/07/2012 13:30', '00:30:00' ) dr
LEFT OUTER JOIN tbl_Bookings me
ON me.DateStarted BETWEEN dr.startdate AND dr.enddate
AND me.DateEnded BETWEEN dr.startdate AND dr.enddate)
GROUP BY dr.startdate, dr.enddate
任何人都可以建议一种更好的方法来实现这一目标,或者希望能够提供一种解决方案来解决我目前正在尝试的方式吗?
提前致谢!
答案 0 :(得分:2)
我相信我在SQL中有一个可行的解决方案。这假设tbl_Bookings
中的数据是一致的,即给定房间没有开始/结束时间重叠。可能是一种更简单的方法,但诀窍是订购预订并将结束时间配对以下开始时间。在指定的Start
之后但在第一次预订之前,有两个额外的查询联合以获取任何间隔。同样适用于End
。
编辑:在WHERE NOT EXISTS
或@Start
落入预定时间间隔内的最后两个查询中添加了@End
个警卫。
DECLARE @Start DateTime = '05/07/2012 08:30'
DECLARE @End DateTime = '05/07/2012 13:30'
;WITH Bookings (RoomId, RowNum, Started, Ended) AS (
SELECT RoomId,
ROW_NUMBER() OVER (PARTITION BY RoomId ORDER BY DateStarted) AS RowNum,
DateStarted, DateEnded
FROM tbl_Bookings
)
SELECT RoomId, B.Ended AS S, C.Started AS E
FROM Bookings B
CROSS APPLY (
SELECT B2.Started FROM Bookings B2
WHERE B2.RowNum = B.RowNum + 1
AND B2.Started <= @End
AND B2.RoomId = B.RoomId
) C
WHERE B.Ended >= @Start
UNION
-- Show any available time from @Start until the next DateStarted, unless @Start
-- falls within a booked interval.
SELECT RoomId, @Start, MIN(DateStarted)
FROM tbl_Bookings
WHERE DateStarted > @Start
AND NOT EXISTS (
SELECT 1 FROM Bookings WHERE Started < @Start AND Ended > @Start
)
GROUP BY RoomId
UNION
-- Show any available time from the last DateEnded to @End, unless @End
-- falls within a booked interval.
SELECT RoomId, MAX(DateEnded), @End
FROM tbl_Bookings
WHERE DateEnded < @End
AND NOT EXISTS (
SELECT 1 FROM Bookings WHERE Started < @End AND Ended > @End
)
GROUP BY RoomId
答案 1 :(得分:0)
我不知道如何使用集合解决问题,但以下基于游标的方法应该有效。这是你在VB或C#中的方式:
CREATE FUNCTION GetAvailableTimes
(
@MinDate datetime,
@MaxDate datetime
)
RETURNS @result TABLE
(
DateStarted datetime,
DateEnded datetime,
RoomID int
)
AS
BEGIN
DECLARE @DateStarted datetime
DECLARE @DateEnded datetime
DECLARE @CurrentDate datetime
DECLARE @RoomID int
DECLARE @CurrentRoom int
DECLARE c CURSOR FOR
SELECT DateStarted, DateEnded, RoomID
FROM tbl_Bookings
WHERE DateStarted BETWEEN @MinDate AND @MaxDate
OR DateEnded BETWEEN @MinDate AND @MaxDate
ORDER BY RoomID, DateStarted
SET @CurrentRoom = 0
OPEN c
FETCH NEXT FROM c
INTO @DateStarted, @DateEnded, @RoomID
WHILE @@FETCH_STATUS = 0
BEGIN
IF @CurrentRoom <> @RoomID BEGIN
IF @CurrentRoom <> 0 AND @CurrentDate < @MaxDate BEGIN
INSERT INTO @result VALUES (@CurrentDate, @MaxDate, @CurrentRoom)
END
SET @CurrentDate = @MinDate
SET @CurrentRoom = @RoomID
END
IF @CurrentDate < @DateStarted BEGIN
INSERT INTO @result VALUES (@CurrentDate, @DateStarted, @CurrentRoom)
END
SET @CurrentDate = @DateEnded
FETCH NEXT FROM c
INTO @DateStarted, @DateEnded, @RoomID
END
CLOSE c
DEALLOCATE c
IF @CurrentRoom <> 0 AND @CurrentDate < @MaxDate BEGIN
INSERT INTO @result VALUES (@CurrentDate, @MaxDate, @CurrentRoom)
END
RETURN
END
以下调用现在将为您提供测试数据所需的结果。
SELECT * FROM dbo.GetAvailableTimes('20120716 8:30', '20120716 13:30')
我还假设可能有多个房间,而且您正在寻找所有房间的可用时间。
我只是很快就测试了这个功能,所以我很确定还有一些边境案例没有得到妥善处理。但你应该明白这一点。
答案 2 :(得分:0)
我会用以下逻辑来解决这个问题。创建所需开始时间与您在数据中看到的第一个开始时间之间的时间段的记录(如果有)。创建所需结束时间与您在数据中看到的最后结束时间之间的时间段的记录(如果有)。然后,根据您的时间创建中间记录。
以下查询有这个想法。当期望的开始和结束时间在预订的时间段中间时,我不确定它是否有效。
with const as (select @starttime as StartTime, @endtime as EndTime)
select *
from ((Select c.StartTime, MIN(DateStarted), RoomId
from tbl_Bookings b cross join const c
where b.DateStarted >= c.StartTime
group by RoomID
having c.StartTime <> MIN(DateStarted)
) union all
(Select max(DateEnded), c.EndTime, RoomId
from tbl_Bookings b cross join const c
where b.DateEnded <= c.EndTime
group by RoomID
having c.EndTime <> max(DateEnded)
) union all
(select *
from (select b.DateEnded as DateStarted, min(b.DateStarted) as DateEnded
from tbl_Bookings b join
tbl_Bookings bnext
on b.RoomId = bnext.RoomId and
bnext.DateStarted > b.DateStarted cross join
const c
where b.DateStarted < c.endtime and
b.DateEnded > c.StartTime and
bnext.DateStart < c.EndTime and
bnext.DateEnded > c.StartTime
group by b.DateEnded
) b cross join const c
where DateStarted <> DateEnded
)
)
最后一个子查询相当复杂。它正在进行自联接以获得与lead()函数等效的函数。