2017-02-16 20 views
0

У меня есть «резервирование» таблица со следующих полямиКак выбрать время, в котором доступна комната, указанная дата и номер в SQL Server?

ReservationID int 
ReservationDateFrom datetime 
ReservationDateTo datetime 
ReservationRoom int 

Я хочу создать зр, который может выбрать доступный номер и время, доступно данной дата и номер. Это то, что мне удалось сделать до сих пор, и это не сработает. Он ничего не возвращает, если на заданную дату нет расписания номеров, и он возвращает только доступные расписания комнат между 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 в ответ на мой зр как так

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 

Case 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 

Case 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 

Фактический результат пуста

+1

ли ваши выборочные данные правильно, особенно для 5 и 6 ReservationID? –

+0

Что-то не так? 5 - с 9:50 до 11:00 и 6 - с 13:00 -3: 00pm –

+0

Строка 6 выглядит так, как должна была к 2017-01-03 вместо 2017-01-02 для FromDate. – Jerrad

ответ

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 будет включать в себя само- присоединяется. Очень уродливый ИМО).

Более подробную информацию о таблице Numbers, проверьте здесь:

https://dba.stackexchange.com/questions/11506/why-are-numbers-tables-invaluable

+0

Как мне интегрировать это в мой sp? Я не знаю, что объекты представляют в моем sp. Я понимаю, что @t - таблица резервирования в моей sp. Что представляют собой показатели utility.numbers? Это дает мне ошибки –

+0

Да, @t представляет таблицу резервирования, которую я редактировал, чтобы отразить это. Я также включил ссылку SE с дополнительной информацией о таблице Numbers (как создать и для чего ее использовать). – dean

+0

Не могли бы вы дать мне точный скрипт для создания и вставки таблицы номеров, на которой вы говорите. Я был бы счастлив проверить это. Я честно не понимаю, как это работает. –

2

Я думаю, что у вас есть ошибка в тестовых данных или в ожидаемом выходе. Кроме того, edgecases не определены и т. Д.

Вот то, что вы можете начать с

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

+0

Я только что узнал, что это не работает. Попробуйте удалить первую запись, и она не отображает доступное расписание до первой записи. 00:00:00 \t 05:00:00 \t 300. Он также не работает, если помещение не зарезервировано в данную дату. Он ничего не возвращает, когда должен возвращать всю дату. Например, когда вы пытаетесь заменить комнату с 14 до 15 –

+0

Я отредактировал ответ, теперь он работает для этих тестовых случаев? Если вы хотите, чтобы он возвращался в 24:00 в течение конечного времени, вам нужно будет отправить время в varchar и добавить предложение для случая. – zerox981

0

Я добавил некоторые примеры данных в таблицу и фиксированной даты. Это не сложное решение, но, надеюсь, оно дает вам некоторые идеи о том, как получить нужные результаты.

Вы можете управлять результатами с помощью цикла или общего выражения таблицы. Я использовал cte вместе с объявленным столом. Вы можете избавиться от объявленной таблицы в моем решении и присоединить рекурсивную часть к тому же, что и базовый запрос, если хотите.

Основная идея - получить конечное время резервирования и время начала следующего в той же строке.

Я только сделал часть выбора, я оставлю вам процедуру для вас.

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 
+0

Вы пропустили последнюю запись с этим решением. 2017-01-02 15: 00: 00.000 \t 2017-01-02 00: 00: 00.000 –