You want to exclude conflicting reservations.
Let's start by considering the requested period (A...B) and a hypothetical reservation (C...D), we can diagram all the possibilities and see there are several possibilities for conflict:
A.....B C.....D OKAY: note B < C never happens otherwise
A........C..B..D NOT OKAY (B inside C..D)
A........C.....D..B NOT OKAY (C inside A..B, D inside A..B)
C..A..D..B NOT OKAY (A inside C..D, D inside A..B)
C.A.B.D NOT OKAY (A inside C..D, B inside C..D)
C.....D A..B OKAY: note A > D never happens otherwise
A...B OKAY (no C...D reservation at all)
So basically no element of the either interval must be inside the other interval:
NOT (
(:dateIn BETWEEN rs.date_in AND rs.date_out)
OR
(:dateOut BETWEEN rs.date_in AND rs.date_out)
OR
(rs.date_in BETWEEN :dateIn AND :dateOut)
OR
(rs.date_out BETWEEN :dateIn AND :dateOut)
)
and this must include the query failing (no rs entry at all).
Seeing the same diagram, we also see that we can just request that B < C or D < A, trusting implicitly that A < B and C < D, for availability.
rs.date_in > :dateOut OR rs.date_out < :dateIn
And since you want rooms for which there is no conflict, you must put the query above in a JOIN clause designed to find conflicts, with LEFT JOIN and the request for "no conflict" (rs info is NULL) in the WHERE, to catch where the JOIN clause is failing:
SELECT rooms.*
FROM rooms
LEFT JOIN reservations ON
(
rooms.id = reservations.room_id
AND (
NOT (reservations.date_in > :dateOut OR reservations.date_out < :dateIn)
)
)
) WHERE rooms.id = :id AND reservations.room_id IS NULL
The JOIN clause can be simplified since NOT (A OR B)
equals NOT A AND NOT B
, and NOT >
is <=
:
(
rooms.id = reservations.room_id
AND reservations.date_in <= :dateOut
AND reservations.date_out >= :dateIn
)
which immediately tells us a convenient index for reservations
:
CREATE INDEX reservations_chk ON reservations(room_id, date_in, date_out, rs_id);
You want to test this and verify what happens when a reservation ends on one day and a request starts on that very same day (and the same with reservation starting on a request ending). Depending on hotel policy, you might then want to drop equality from one or both restriction clauses. This is because database days "start" and "end" at midnight, but real-life reservations might begin at, say, 11:00 and end at 10:00 to allow for room cleanup and preparation.
Seeing as such a query is likely to contain keywords SELECT, JOIN, reservation, room, available etc, you can google and find also documents such as this or this, or this on Stack Overflow, which might be of interest (I've only given a cursory look at them - caveat emptor).