次のことを考慮してください。
DROP TABLE IF EXISTS apartment;
CREATE TABLE apartment
(apartment_id SERIAL PRIMARY KEY
,apartment_name varchar(255) NOT NULL
);
INSERT INTO apartment VALUES
(1, 'Apartment 1'),
(2, 'Apartment 2'),
(3, 'Apartment 3'),
(4, 'Apartment 4');
DROP TABLE IF EXISTS booking;
CREATE TABLE booking
(booking_id SERIAL PRIMARY KEY
,start_date DATE NOT NULL
,end_date DATE NOT NULL
,apartment_id INT NOT NULL
);
INSERT INTO booking VALUES
(1, '2018-07-18', '2018-07-20', 1),
(2, '2018-07-21', '2018-07-23', 1),
(3, '2018-07-18', '2018-07-20', 2);
SELECT a.*
FROM apartment a
LEFT
JOIN booking b
ON b.apartment_id = a.apartment_id
AND b.start_date <= '2018-07-23'
AND b.end_date > '2018-07-21'
WHERE b.booking_id IS NULL;
+--------------+----------------+
| apartment_id | apartment_name |
+--------------+----------------+
| 2 | Apartment 2 |
| 3 | Apartment 3 |
| 4 | Apartment 4 |
+--------------+----------------+
実際には、トランザクション内に拘束されない限り、このようなSELECTはかなり意味がありません。これは、他のユーザーが簡単に来て、空室状況を確認している間に予約を行うことができるためです。