I have to entities: postes
and bookings
. There is a oneToMany relationship between poste
and booking
: one poste may have many bookings (at different dates).
bookings
is defined by 4 columns:
booking_id
: idposte_id
: jointurepostes
tablestart_datetime
: start date bookingnumber_day
: number of days (integer)
postes
is defined by 4 columns:
poste_id
: poste idpattern
(string): defined the allowed day (1 is allowed, 0 no). The 8th day is defined as the 1st day of the pattern (modulo 7)start
: poste beginning date (all the date in bookings are included between start and end)end
: poste end date
Goal: I would like to define a query that selects all postes
that are not fully reserved (e.g. some new reservations are possible). I'm stuck because I can't select any data for the free date range since only bookings are stored.
Example
Booking table
| booking_id | poste_id | start_datetime | number_day |
|------------|----------|----------------------|------------|
| 1 | 1 | 2019-07-10T00:00:00Z | 4 |
| 4 | 1 | 2019-07-14T00:00:00Z | 1 |
| 7 | 1 | 2019-07-16T00:00:00Z | 4 |
| 2 | 2 | 2019-07-10T00:00:00Z | 2 |
| 9 | 2 | 2019-07-13T00:00:00Z | 2 |
| 5 | 3 | 2019-07-15T00:00:00Z | 2 |
| 8 | 3 | 2019-07-21T00:00:00Z | 3 |
| 11 | 3 | 2019-07-28T00:00:00Z | 1 |
| 12 | 3 | 2019-07-29T00:00:00Z | 1 |
| 3 | 4 | 2019-07-15T00:00:00Z | 1 |
| 13 | 4 | 2019-07-21T00:00:00Z | 2 |
Postes table:
| poste_id | pattern | start | end |
|----------|---------|----------------------|----------------------|
| 1 | 1111101 | 2019-07-10T00:00:00Z | 2019-07-20T00:00:00Z |
| 2 | 1101101 | 2019-07-10T00:00:00Z | 2019-07-20T00:00:00Z |
| 3 | 1100001 | 2019-07-15T00:00:00Z | 2019-07-30T00:00:00Z |
| 4 | 1011001 | 2019-07-15T00:00:00Z | 2019-07-30T00:00:00Z |
The expected output of the example is : 2,4
. (no more booking are available for the poste 1
and 3
).
Note:
- This is a simple example. Actually, the date ranges are bigger e.g. over several months.
- The database assumes bookings do not overlap.
- The pattern length might be different from 7. There are no link between the first day of the pattern and the number of days in a week. For example, if the pattern is '1101' with a starting date of "10-07-2019", that means the days 10, 11, 13, 14, 15, 17 and so on are available, not the others.
- The booking dates are always between the starting and ending date of the
poste
.
Reproductibility:
// Build the tables:
CREATE TABLE bookings
(`booking_id` int, `poste_id` int, `start_datetime` datetime, `number_day` int)
;
INSERT INTO bookings
(`booking_id`, `poste_id`, `start_datetime`, `number_day`)
VALUES
(1, 1, '2019-07-10', '4'),
(4, 1, '2019-07-14', '1'),
(7, 1, '2019-07-16', '4'),
(2, 2, '2019-07-10', '2'),
(9, 2, '2019-07-13', '2'),
(5, 3, '2019-07-15', '2'),
(8, 3, '2019-07-21', '3'),
(11, 3, '2019-07-28', '1'),
(12, 3, '2019-07-29', '1'),
(3, 4, '2019-07-15', '1'),
(13, 4, '2019-07-21', '2')
;
CREATE TABLE postes
(`poste_id` int, `pattern` VARCHAR(7), `start` datetime, `end` datetime);
INSERT INTO postes VALUES
(1, "1111101", "2019-07-10", "2019-07-20"),
(2, "1101101", "2019-07-10", "2019-07-20"),
(3, "1100001", "2019-07-15", "2019-07-30"),
(4, "1011001", "2019-07-15", "2019-07-30");
My work: so far, I managed to find for a given day the available poste:
SELECT DISTINCT p.*
FROM postes p
LEFT JOIN bookings b
ON b.poste_id = p.poste_id
WHERE
/* Ignore date in past */
MOD(DATEDIFF("2019-07-16", p.start), LENGTH(p.pattern)) >= -1
AND
/* Filter poste with pattern = 1 */
SUBSTRING(p.pattern, MOD(DATEDIFF("2019-07-16", p.start),
LENGTH(p.pattern)) + 1 , 1) = 1
AND
/* Filter those available this day */
p.poste_id NOT IN (
SELECT b.poste_id
FROM bookings b
WHERE b.start_datetime <= "2019-07-16"
AND "2019-07-16" < DATE_ADD(b.start_datetime, INTERVAL b.number_day DAY)
);
Output:
| poste_id | pattern | start | end |
|----------|---------|----------------------|----------------------|
| 2 | 1101101 | 2019-07-10T00:00:00Z | 2019-07-20T00:00:00Z |