Search code examples
mysqldateintervals

mysql show time slots avaliable and time slots busy from table


i have this table structure for bookings

|ID|timeBooked         | duration |
|2 |2013-05-09 11:10:00| 30       |
|1 |2013-05-09 14:40:00| 15       |
|AI| timespan          | int(4)   |

duration represent the duration in minutes.

so what i want is to return record set like this when i

query available time slots in 2013-05-09 between 00:00:00 and 23:00:00

|free_from|Free_until| Free |
|00:00:00 |11:10:00  | 1
|11:10:00 |11:40:00  | 0
|11:40:00 |14:40:00  | 1
|14:40:00 |14:55:00  | 0
|14:55:00 |23:00:00  | 1

is this possible by mysql alone ?


Solution

  • OK, pure MySQL - as long as one likes those tricks. I need a Variable which is initialized to the "Begin" of the periods to show, something like now() normally.

    First just the test data:

    create table bookingEvents 
       (id int not null primary key auto_increment,
        timeBooked datetime, 
        duration int
       );
    
    
    insert into bookingEvents values (null, '2013-05-13 13:22:00', 15);
    insert into bookingEvents values (null, '2013-05-13 15:10:00', 45);
    insert into bookingEvents values (null, '2013-05-13 19:55:00', 30);
    insert into bookingEvents values (null, '2013-05-14 03:22:00', 15);
    insert into bookingEvents values (null, '2013-05-14 08:19:00', 15);
    

    Then initializing the "slider":

    set @timeSlider='2013-05-10 00:00:00';
    

    Then the select:

    select if (d.name = 'Free', @timeSlider, b.timeBooked) as free_from,
           if (d.name = 'Free', b.timeBooked, @timeSlider := b.timeBooked + interval b.duration minute) as free_until,
           d.name as Free
    from (select 1 as place, 'Free' as name union select 2 as place, 'Booked' as name) d 
    inner join bookingEvents b 
    having free_from < free_until
    order by b.timeBooked, d.place;
    

    Result:

    +---------------------+---------------------+--------+
    | free_from           | free_until          | Free   |
    +---------------------+---------------------+--------+
    | 2013-05-10 00:00:00 | 2013-05-13 13:22:00 | Free   |
    | 2013-05-13 13:22:00 | 2013-05-13 13:37:00 | Booked |
    | 2013-05-13 13:37:00 | 2013-05-13 15:10:00 | Free   |
    | 2013-05-13 15:10:00 | 2013-05-13 15:55:00 | Booked |
    | 2013-05-13 15:55:00 | 2013-05-13 19:55:00 | Free   |
    | 2013-05-13 19:55:00 | 2013-05-13 20:25:00 | Booked |
    | 2013-05-13 20:25:00 | 2013-05-14 03:22:00 | Free   |
    | 2013-05-14 03:22:00 | 2013-05-14 03:37:00 | Booked |
    | 2013-05-14 03:37:00 | 2013-05-14 08:19:00 | Free   |
    | 2013-05-14 08:19:00 | 2013-05-14 08:34:00 | Booked |
    +---------------------+---------------------+--------+
    

    If you have a given End-Timestamp, then you have to pre-set that as @timeMaximum

    set @timeSlider='2013-05-10 00:00:00';
    set @timeMaximum='2013-05-14 08:35:00';
    
    
    select if (d.name = 'Free', @timeSlider, b.timeBooked) as free_from,
           if (d.name = 'Free', b.timeBooked, @timeSlider := b.timeBooked + interval b.duration minute) as free_until,
           d.name as Free
    from (select 1 as place, 'Free' as name union select 2 as place, 'Booked' as name ) as d 
    inner join bookingEvents b 
    having free_from < free_until
    union select @timeSlider as free_from, @timeMaximum as free_until, 'Free' as Free
    from (select 1) as d
    where @timeSlider < @timeMaximum
    
    order by free_from, free_until
    ;