3

I'm building up a website where you can take an appointement online. I'll not explain all in detail but I have an table with my available time to take an appointment. Distributed into intervals of 5 min. Here's an example:

ID      StartDate               EndDate
492548  2016-12-16 08:00:00.000 2016-12-16 08:05:00.000
492549  2016-12-16 08:05:00.000 2016-12-16 08:10:00.000
492550  2016-12-16 08:10:00.000 2016-12-16 08:15:00.000
492551  2016-12-16 08:15:00.000 2016-12-16 08:20:00.000
492552  2016-12-16 08:20:00.000 2016-12-16 08:25:00.000
492553  2016-12-16 08:25:00.000 2016-12-16 08:30:00.000
492554  2016-12-16 08:30:00.000 2016-12-16 08:35:00.000
492555  2016-12-16 08:35:00.000 2016-12-16 08:40:00.000
492556  2016-12-16 08:40:00.000 2016-12-16 08:45:00.000
492557  2016-12-16 08:45:00.000 2016-12-16 08:50:00.000
492558  2016-12-16 08:50:00.000 2016-12-16 08:55:00.000
492559  2016-12-16 08:55:00.000 2016-12-16 09:00:00.000
492560  2016-12-16 09:00:00.000 2016-12-16 09:05:00.000
492561  2016-12-16 09:05:00.000 2016-12-16 09:10:00.000
492562  2016-12-16 09:10:00.000 2016-12-16 09:15:00.000
492563  2016-12-16 09:15:00.000 2016-12-16 09:20:00.000
492564  2016-12-16 09:20:00.000 2016-12-16 09:25:00.000
492565  2016-12-16 09:25:00.000 2016-12-16 09:30:00.000
492566  2016-12-16 09:30:00.000 2016-12-16 09:35:00.000

Depending the consultation time, based in the reason for consultation, I have to group these rows into one and know the min(IDSchedulingInterval) and the max(IDSchedulingInterval).

Here's an example of the result I want if I have a duration time of 15 min:

Min(ID) Max(ID) StartDate               EndDate
492548  492550  2016-12-16 08:00:00.000 2016-12-16 08:15:00.000
492551  492553  2016-12-16 08:15:00.000 2016-12-16 08:30:00.000
492554  492556  2016-12-16 08:30:00.000 2016-12-16 08:45:00.000
492557  492559  2016-12-16 08:45:00.000 2016-12-16 09:00:00.000

The duration time can change. I don't know how to proceed to make this query..

EDIT Here are some exception you have to check. Here's my table

ID      StartDate               EndDate                  Isreserved
492548  2016-12-16 08:00:00.000 2016-12-16 08:05:00.000  0  
492549  2016-12-16 08:05:00.000 2016-12-16 08:10:00.000  0  
492550  2016-12-16 08:10:00.000 2016-12-16 08:15:00.000  0  
492551  2016-12-16 08:15:00.000 2016-12-16 08:20:00.000  0  
492552  2016-12-16 08:20:00.000 2016-12-16 08:25:00.000  0      
492555  2016-12-16 08:35:00.000 2016-12-16 08:40:00.000  0  
492556  2016-12-16 08:40:00.000 2016-12-16 08:45:00.000  0  
492557  2016-12-16 08:45:00.000 2016-12-16 08:50:00.000  1  
492558  2016-12-16 08:50:00.000 2016-12-16 08:55:00.000  1  
492559  2016-12-16 08:55:00.000 2016-12-16 09:00:00.000  1  
492560  2016-12-16 09:00:00.000 2016-12-16 09:05:00.000  0  
492561  2016-12-16 09:05:00.000 2016-12-16 09:10:00.000  0  
492562  2016-12-16 09:10:00.000 2016-12-16 09:15:00.000  0  
492563  2016-12-16 09:15:00.000 2016-12-16 09:20:00.000  0  
492564  2016-12-16 09:20:00.000 2016-12-16 09:25:00.000  0  
492565  2016-12-16 09:25:00.000 2016-12-16 09:30:00.000  0  
492566  2016-12-16 09:30:00.000 2016-12-16 09:35:00.000  0  

Here the time between 8:45 to 9:00 is reserved so you can't take it. Also you don't have time between 8:25 and 8:35 so you can't reserved it either. An example, if I want to take a appointment of 30 min then I should have a result like this one:

Min(ID) Max(ID) StartDate               EndDate
492560  492565  2016-12-16 09:00:00.000 2016-12-16 09:30:00.000

Only 1 row will be returned because you don't have enough time between other intervals

EDIT 2

Thanks to DVT I have modified is query and i'm almost having my query work the only hic here is the overlapping time. here's my query:

DECLARE @newinterval INT = 60;

;with cte as (
SELECT
    t1.IdSchedulingByInterval AS IdSchedulingByIntervalMin
    , t2.IdSchedulingByInterval AS IdSchedulingByIntervalMax
    , t1.SchedulingByIntervalStartDate 
    , t2.SchedulingByIntervalEndDate
FROM
   RDV_tbSchedulingByInterval t1
    JOIN RDV_tbSchedulingByInterval t2 ON t2.SchedulingByIntervalStartDate = DATEADD(minute, @newinterval - 5, t1.SchedulingByIntervalStartDate)
    ) select * from cte where (select SUM(5) from RDV_tbSchedulingByInterval where IdSchedulingByInterval 
                                between cte.IdSchedulingByIntervalMin  and cte.IdSchedulingByIntervalMax ) = @newinterval
    order by cte.SchedulingByIntervalStartDate

Here's my result:

492551  492562  2016-12-16 08:15:00.000 2016-12-16 09:15:00.000
492552  492563  2016-12-16 08:20:00.000 2016-12-16 09:20:00.000
492553  492564  2016-12-16 08:25:00.000 2016-12-16 09:25:00.000
492554  492565  2016-12-16 08:30:00.000 2016-12-16 09:30:00.000
492555  492566  2016-12-16 08:35:00.000 2016-12-16 09:35:00.000
492556  492567  2016-12-16 08:40:00.000 2016-12-16 09:40:00.000
492557  492568  2016-12-16 08:45:00.000 2016-12-16 09:45:00.000
492558  492569  2016-12-16 08:50:00.000 2016-12-16 09:50:00.000
492559  492570  2016-12-16 08:55:00.000 2016-12-16 09:55:00.000
492560  492571  2016-12-16 09:00:00.000 2016-12-16 10:00:00.000
492561  492572  2016-12-16 09:05:00.000 2016-12-16 10:05:00.000
492562  492573  2016-12-16 09:10:00.000 2016-12-16 10:10:00.000
492563  492574  2016-12-16 09:15:00.000 2016-12-16 10:15:00.000
492564  492575  2016-12-16 09:20:00.000 2016-12-16 10:20:00.000
492565  492576  2016-12-16 09:25:00.000 2016-12-16 10:25:00.000
492566  492577  2016-12-16 09:30:00.000 2016-12-16 10:30:00.000
492567  492578  2016-12-16 09:35:00.000 2016-12-16 10:35:00.000
492568  492579  2016-12-16 09:40:00.000 2016-12-16 10:40:00.000
492569  492580  2016-12-16 09:45:00.000 2016-12-16 10:45:00.000

Expected result:

492551  492562  2016-12-16 08:15:00.000 2016-12-16 09:15:00.000
492563  492574  2016-12-16 09:15:00.000 2016-12-16 10:15:00.000

I don't want time to overlapped other

alexandre
  • 297
  • 4
  • 16
  • please post sample and expected results as text – TheGameiswar Dec 19 '16 at 13:36
  • @TheGameiswar the sample is the first picture and the expected result is the second picture – alexandre Dec 19 '16 at 13:40
  • images are blocked in my domain,also posting as text helps in preparing sample data:http://meta.stackoverflow.com/questions/261455/imgur-com-blocked-what-are-my-options – TheGameiswar Dec 19 '16 at 13:42
  • @TheGameiswar I just edit my post No more Image only text – alexandre Dec 19 '16 at 13:46
  • what if there is no data ,i mean no row exist for example say no startdate,edndate exists for 8:15 – TheGameiswar Dec 19 '16 at 13:51
  • Is the parametrization of the query really that important? What I mean is that you can build 3 or 4 querys with different time intervals and manage the call of the query in the website. So, if you have a consultation that need 30 minutes, call the query that looks for 30 minutes interval, if you have a 15 minute consultation call the 15 minute query and so on.. It's not the right way to do it, but it is the best way to do it. Engineering needs to be pragmatical. – Nambu14 Dec 19 '16 at 15:57

7 Answers7

1

As your session duration can change, here is a slightly more flexible approach:

declare @t table (IDSchedulingByInterval int identity(1,1)
                    ,SchedulingByIntervalStartDate datetime
                    ,SchedulingByIntervalEndDate datetime
                    );
insert into @t(SchedulingByIntervalStartDate, SchedulingByIntervalEndDate)
values('2016-12-16 08:00:00.000','2016-12-16 08:05:00.000'),('2016-12-16 08:05:00.000','2016-12-16 08:10:00.000'),('2016-12-16 08:10:00.000','2016-12-16 08:15:00.000'),('2016-12-16 08:15:00.000','2016-12-16 08:20:00.000'),('2016-12-16 08:20:00.000','2016-12-16 08:25:00.000'),('2016-12-16 08:25:00.000','2016-12-16 08:30:00.000'),('2016-12-16 08:30:00.000','2016-12-16 08:35:00.000'),('2016-12-16 08:35:00.000','2016-12-16 08:40:00.000'),('2016-12-16 08:40:00.000','2016-12-16 08:45:00.000'),('2016-12-16 08:45:00.000','2016-12-16 08:50:00.000'),('2016-12-16 08:50:00.000','2016-12-16 08:55:00.000'),('2016-12-16 08:55:00.000','2016-12-16 09:00:00.000'),('2016-12-16 09:00:00.000','2016-12-16 09:05:00.000'),('2016-12-16 09:05:00.000','2016-12-16 09:10:00.000'),('2016-12-16 09:10:00.000','2016-12-16 09:15:00.000'),('2016-12-16 09:15:00.000','2016-12-16 09:20:00.000'),('2016-12-16 09:20:00.000','2016-12-16 09:25:00.000'),('2016-12-16 09:25:00.000','2016-12-16 09:30:00.000'),('2016-12-16 09:30:00.000','2016-12-16 09:35:00.000'),('2016-12-16 09:35:00.000','2016-12-16 09:40:00.000'),('2016-12-16 09:40:00.000','2016-12-16 09:45:00.000'),('2016-12-16 09:45:00.000','2016-12-16 09:50:00.000'),('2016-12-16 09:50:00.000','2016-12-16 09:55:00.000'),('2016-12-16 09:55:00.000','2016-12-16 10:00:00.000');

declare @Interval int = 15; -- This is the number of minutes for each session.  Must be divisible by 5 as base data is at a 5 minute granualarity.

select s.IDSchedulingByInterval as MinIDSchedulingByInterval
        ,e.IDSchedulingByInterval as MaxIDSchedulingByInterval
        ,s.SchedulingByIntervalStartDate
        ,e.SchedulingByIntervalEndDate
from @t s
    left join @t e  -- Find the corresponding end time for the session's start time
        on(dateadd(minute,@Interval,s.SchedulingByIntervalStartDate) =  e.SchedulingByIntervalEndDate)
where datediff(minute
                ,(select min(SchedulingByIntervalStartDate) from @t)
                ,s.SchedulingByIntervalStartDate
                ) % @Interval = 0;      -- This is the check that start time is at the start of one of your defined intervals.

Updated to include logic for unavailable periods:

declare @t table (IDSchedulingByInterval int identity(1,1)
                    ,SchedulingByIntervalStartDate datetime
                    ,SchedulingByIntervalEndDate datetime
                    ,Reserved bit
                    );
insert into @t(SchedulingByIntervalStartDate, SchedulingByIntervalEndDate,Reserved)
values('2016-12-16 08:00:00.000','2016-12-16 08:05:00.000',0),('2016-12-16 08:05:00.000','2016-12-16 08:10:00.000',0),('2016-12-16 08:10:00.000','2016-12-16 08:15:00.000',0),('2016-12-16 08:15:00.000','2016-12-16 08:20:00.000',0),('2016-12-16 08:20:00.000','2016-12-16 08:25:00.000',0),('2016-12-16 08:25:00.000','2016-12-16 08:30:00.000',0),('2016-12-16 08:30:00.000','2016-12-16 08:35:00.000',0),('2016-12-16 08:35:00.000','2016-12-16 08:40:00.000',0),('2016-12-16 08:40:00.000','2016-12-16 08:45:00.000',0),('2016-12-16 08:45:00.000','2016-12-16 08:50:00.000',1),('2016-12-16 08:50:00.000','2016-12-16 08:55:00.000',1),('2016-12-16 08:55:00.000','2016-12-16 09:00:00.000',1),('2016-12-16 09:00:00.000','2016-12-16 09:05:00.000',0),('2016-12-16 09:05:00.000','2016-12-16 09:10:00.000',0),('2016-12-16 09:10:00.000','2016-12-16 09:15:00.000',0),('2016-12-16 09:15:00.000','2016-12-16 09:20:00.000',0),('2016-12-16 09:20:00.000','2016-12-16 09:25:00.000',0),('2016-12-16 09:25:00.000','2016-12-16 09:30:00.000',0),('2016-12-16 09:30:00.000','2016-12-16 09:35:00.000',0),('2016-12-16 09:35:00.000','2016-12-16 09:40:00.000',0),('2016-12-16 09:40:00.000','2016-12-16 09:45:00.000',0),('2016-12-16 09:45:00.000','2016-12-16 09:50:00.000',0),('2016-12-16 09:50:00.000','2016-12-16 09:55:00.000',0),('2016-12-16 09:55:00.000','2016-12-16 10:00:00.000',0);

declare @Interval int = 60; -- This is the number of minutes for each session.  Must be divisible by 5 as base data is at a 5 minute granualarity.

with cte
as
(
    select s.IDSchedulingByInterval as MinIDSchedulingByInterval
            ,e.IDSchedulingByInterval as MaxIDSchedulingByInterval
            ,s.SchedulingByIntervalStartDate
            ,e.SchedulingByIntervalEndDate
    from @t s
        left join @t e  -- Find the corresponding end time for the session's start time
            on(dateadd(minute,@Interval,s.SchedulingByIntervalStartDate) =  e.SchedulingByIntervalEndDate)
    where datediff(minute
                    ,(select min(SchedulingByIntervalStartDate) from @t)
                    ,s.SchedulingByIntervalStartDate
                    ) % @Interval = 0       -- This is the check that start time is at the start of one of your defined intervals.
)
select c.MinIDSchedulingByInterval
        ,c.MaxIDSchedulingByInterval
        ,c.SchedulingByIntervalStartDate
        ,c.SchedulingByIntervalEndDate
from cte c
    left join @t t
        on(t.SchedulingByIntervalStartDate <= c.SchedulingByIntervalEndDate
            and t.SchedulingByIntervalEndDate > c.SchedulingByIntervalStartDate
            )
group by c.MinIDSchedulingByInterval
        ,c.MaxIDSchedulingByInterval
        ,c.SchedulingByIntervalStartDate
        ,c.SchedulingByIntervalEndDate
having sum(cast(t.Reserved as int)) = 0
iamdave
  • 12,023
  • 3
  • 24
  • 53
  • Your query is almost working and fast but I test it and the only problem I have is if you want an interval of 60 minute by example and you already have an 15 min time reserve in this time lapse example: from 8:00 to 8:15 it's reserved you'Re query will still return me 8:00 to 9:00 to be available – alexandre Dec 19 '16 at 15:11
  • @alexandre I have updated my answer to not return any periods with booked slots within them. – iamdave Dec 19 '16 at 15:58
  • It's not working either it's giving me the interval 8:00 to 9:00 but in this interval I have an missing interval that is 8:10 to 8:15 so oyu don't have a full interval of 60 minute – alexandre Dec 19 '16 at 16:04
  • @alexandre Ah I think I know why, I have edited my code to account for this. – iamdave Dec 19 '16 at 16:44
1
-- This converts the period to date-time format
SELECT 
    -- note the 15, the "minute", and the starting point to convert the 
    -- period back to original time
    DATEADD(minute, AP.FifteenMinutePeriod * 15, '2010-01-01T00:00:00') AS Period,
    AP.AvgValue
FROM
    -- this groups by the period and gets the average
    (SELECT
        P.FifteenMinutePeriod,
        AVG(P.Value) AS AvgValue
    FROM
        -- This calculates the period (fifteen minutes in this instance)
        (SELECT 
            -- note the division by 15 and the "minute" to build the 15 minute periods
            -- the '2010-01-01T00:00:00' is the starting point for the periods
            datediff(minute, '2010-01-01T00:00:00', T.Time)/15 AS FifteenMinutePeriod,
            T.Value
        FROM Test T) AS P
    GROUP BY P.FifteenMinutePeriod) AP
Jonathan Porter
  • 1,365
  • 7
  • 34
  • 62
1

This handles a variable minute interval. Note this hasn't been performance tested:

DECLARE @MinuteInterval INT = 15

SELECT      MIN(a.IdSchedulingByInterval) AS MinId,
            MAX(aa.IdSchedulingByInterval) AS MaxId,
            MIN(a.SchedulingByIntervalStartDate) AS StartDate,
            MAX(aa.SchedulingByIntervalEndDate) AS EndDate
FROM        Appointment a
CROSS JOIN  Appointment aa 
WHERE       DATEDIFF(MINUTE, a.SchedulingByIntervalStartDate, aa.SchedulingByIntervalEndDate) = @MinuteInterval
GROUP BY    a.IdSchedulingByInterval
HAVING      DATEPART(MINUTE, MIN(a.SchedulingByIntervalStartDate)) % @MinuteInterval = 0
J.M Smith
  • 381
  • 2
  • 8
1

This is the easiest i could come up with without more specifics

;WITH Tally AS
 (
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS N FROM master.sys.objects A, master.sys.objects B
 )
,Intervals AS
(
    SELECT
    N AS Id,
    DATEADD(MINUTE, (N-1)*5, '20160101') AS StartDate,
    DATEADD(MINUTE, (N)*5, '20160101') AS EndDate
    FROM Tally
)

SELECT MIN(Id) AS MinId, MAX(Id) AS MaxId, MIN(StartDate), MAX(EndDate) FROM Intervals
GROUP BY CAST(StartDate AS Date), DATEPART(HOUR,StartDate), DATEPART(MINUTE, StartDate) / 15
ORDER BY MinId

EDIT:

Just replace the names with your table like

SELECT 
MIN(IdSchedulingByInterval) AS MinId, 
MAX(IdSchedulingByInterval) AS MaxId, 
MIN(SchedulingByIntervalStartDate), ¨
MAX(SchedulingByIntervalEndDate) 
FROM RDV_tbSchedulingByInterval 
GROUP BY CAST(SchedulingByIntervalStartDate AS Date), DATEPART(HOUR,SchedulingByIntervalStartDate), DATEPART(MINUTE, SchedulingByIntervalStartDate) / 15
ORDER BY MinId
CodeMonkey
  • 3,418
  • 4
  • 30
  • 53
1

I think you can use a query like the following:

SELECT MIN(ID) AS minID, MAX(ID) AS maxID, 
       MIN(StartDate) AS StartDate, MAX(EndDate) AS EndDate
FROM (
   SELECT ID, StartDate, EndDate,       
          ROW_NUMBER() OVER (ORDER BY StartDate) - 
          ROW_NUMBER() OVER (PARTITION BY x.v
                             ORDER BY StartDate) AS grp
   FROM mytable
   CROSS APPLY (SELECT CAST(CONVERT(DATE, EndDate) AS VARCHAR(10)) + 
                       CAST(DATEPART(HOUR, StartDate) AS VARCHAR(10)) + 
                       CAST(DATEPART(MINUTE, StartDate) / 15 AS VARCHAR(10))) AS x(v)) AS t
GROUP BY t.grp                                              
ORDER BY EndDate
Giorgos Betsos
  • 71,379
  • 9
  • 63
  • 98
  • It's almost working the only hic to this is it's possible there's missing row because some ppl wouldnt want to work from 12h to 13h because they have launch so the 12h to 13 interval wouldnt appear or even certain row will be reserved already by another appointment but your query would not look up at this – alexandre Dec 19 '16 at 14:37
  • @alexandre Can you post some sample data demonstrating the issue ? – Giorgos Betsos Dec 19 '16 at 15:19
  • I have edit my question with an example of exception – alexandre Dec 19 '16 at 15:37
0
DECLARE @newinterval INT = 15;

SELECT
    t1.IdSchedulingByInterval AS IdSchedulingByIntervalMin
    , t2.IdSchedulingByInterval AS IdSchedulingByIntervalMax
    , t1.SchedulingByIntervalStartDate 
    , t2.SchedulingByIntervalEndDate
FROM
    <table> t1
    JOIN <table> t2 ON t2.SchedulingByIntervalStartDate = DATEADD(minute, @newinterval - 5, t1.SchedulingByIntervalStartDate)
WHERE
    DATEPART(minute,t1.SchedulingByIntervalStartDate) % @newinterval = 0;
DVT
  • 3,014
  • 1
  • 13
  • 19
  • Nice one! Just to know what if have different timeduration whas should I change in this query ? – alexandre Dec 19 '16 at 13:53
  • I mean now you're grouping into 15 min interval but if I need to group it into 30 min interval what parameter I must change to achieve this with your answer – alexandre Dec 19 '16 at 13:57
  • I edited the answer to include more flexible case. (It still assume your original interval is 5 minutes) – DVT Dec 19 '16 at 14:00
  • It's a bit slow it takes me 42 for 40 000 records :$ – alexandre Dec 19 '16 at 14:11
  • @alexandre That is a total different problem than what you asked. The performance of a query can be depended on stuff like index of the table, fragmentation, statistics... In the first one, I commented out the one that used t1.Id...=t2.Id... - 2, which would improve performance. But it assumed the PK (clustered) of the table (not stated in the question), and the consecutiveness of the interval running. You can figure out similar methods for the case of 30 cases. – DVT Dec 19 '16 at 14:20
  • you're query is almost doing what I want the only hic is i don't wanna have time overllapping. Example : If i have this : 12:00 to 12:15 I don't want this: 12:05 to 12:20 I need to continue the series like this : 12:00 to 12:15 12:15 to 12:30 12:30 to 12:45 ... to ... – alexandre Dec 19 '16 at 15:03
  • @alexandre fixed. – DVT Dec 19 '16 at 18:28
  • I already tried this solution it's not working because if you do that the overlapped time doesnt come's out.. yes I know i said i didn't want overlapped time but After sorting my data because some interval will have missing interval in it example in 8:00 to 9:00 maybe 8:10 to 8:15 will not be available so I'll need to take 8:15 to 9:15 instead – alexandre Dec 19 '16 at 18:36
0

The simplest query I can think of..

    select MIN(ID), MAX(ID), MIN(StartDate), MAX(EndDate)
    from 
        (
        select 
            ID,
            case
               when substring(CONVERT(varchar, StartDate),16,2)in ('00','05','10') then 1
               when substring(CONVERT(varchar, StartDate),16,2)in ('15','20','25') then 2
               when substring(CONVERT(varchar, StartDate),16,2)in ('30','35','40') then 3
               when substring(CONVERT(varchar, StartDate),16,2)in ('45','50','55') then 4
            end as MinOfDate,
            substring(CONVERT(varchar, StartDate),13,2)as HourOfDate,
            substring(CONVERT(varchar, GETDATE()),1,6) as DayOfDate
            StartDate,
            EndDate
        from SourceTable
        where IsReserved = 0
        ) t
    group by DayOfDate,HourOfDate,MinOfDate
Nambu14
  • 380
  • 1
  • 7
  • 20
  • You should be using the `minute()` function if you need to get the minute part out of a `datetime` value, not converting it to a string. – iamdave Dec 19 '16 at 16:52
  • Fair point, but I have had problems with time functions in the past and found the string conversion more reliable. – Nambu14 Dec 19 '16 at 16:59
  • Problems like what? – iamdave Dec 19 '16 at 17:02
  • It failed in returning the right minutes, I never get to know why or how but that function presented a probabilistic behavior which force me to never used it again – Nambu14 Dec 19 '16 at 20:07
  • You may want to have another look at it, as that function literally does one thing - Extract the Minute element of a `datetime` or `time` data type. If it was giving you the wrong values either you were using it wrong or you were doing something you weren't totally aware of. Of course, if you can give me an example of that function giving the wrong minutes, I will change my mind. – iamdave Dec 20 '16 at 09:24