I’m building a simple availability calendar with PHP and MySQL.
I have a table which stores the available dates for a property (currently all of them are blocks of 7 days)
available_dates:
start_date DATE
end_date DATE
available_id INT PRIMARY KEY
property_id INT
booked TINYINT(1)
And a table of booked dates which references the available_id of my available_dates table:
bookings
booking_id INT
available_id INT
***user details***
I plan on having rows added to available_dates for each property to mark which dates can be booked, and then setting the booked flag on that table when somebody books that block.
What I’d like to do is show a list of dates (in blocks of x days, 7 in this case) that have no availability set – so the date does not appear in that table – for the next 24 months or so.
I’m having trouble wrapping my head around this and I know there is a simpler way to do it that my first ideas of looping through each property, then each block of 7 days, etc etc.
Can anyone enlighten me?
Update:
Thanks to @ZaneBien ‘s brilliant and comprehensive answer, I’ve managed to get the results I need by using his yeardate table & procedure.
What I’ve done is when the page that needs to show the dates with no availability set is requested, the PHP will call the procedure to add more yeardates if there aren’t any for CURYEAR()+2.
Then to get my results, a slightly modified version of Zane’s query:
SELECT
a.yeardate AS blockstart,
DATE_ADD(a.yeardate, INTERVAL 7 DAY) AS blockend
FROM
yeardates a
LEFT JOIN
available_dates b
ON(a.yeardate BETWEEN b.start_date AND b.end_date)
OR
(DATE_ADD(a.yeardate, INTERVAL 7 DAY) BETWEEN b.start_date AND b.end_date)
WHERE
b.date_id IS NULL AND WEEKDAY(a.yeardate)=5;
In my case, the blocks are of 7 days, saturday to saturday – so I added the second WHERE clause to the query so that I get distinct 1 week saturday to saturday blocks for each row, that happen one after the other.
So instead of:
+------------+------------+
| blockstart | blockend |
+------------+------------+
| 2012-01-01 | 2012-01-08 |
| 2012-01-02 | 2012-01-09 |
| 2012-01-03 | 2012-01-10 |
| 2012-01-04 | 2012-01-11 |
I get this:
+------------+------------+
| blockstart | blockend |
+------------+------------+
| 2012-01-07 | 2012-01-14 |
| 2012-01-14 | 2012-01-21 |
| 2012-01-21 | 2012-01-28 |
| 2012-01-28 | 2012-02-04 |
Which is exactly what I need. Thanks again to Zane for a great answer.
Understanding your question as Retrieve all 7 day interval blocks of the current and next year whose ranges do not overlap any interval blocks already existing in the
available_datestable:To work with all days of the current and next year, we have to create a separate table (
yeardates) containingDATEs of all days of the current and next year. This will facilitate ourOUTER JOINoperation in the retrieval query.Code to define the
yeardatestable and insert dates:The table is then created and contains all days of the current and next year. If we ever need to insert days for subsequent years, just
CALLthe procedure again with the year as the parameter (e.g. 2014, 2015, etc..).Then we can get the 7-day blocks that don’t overlap blocks in the available_dates table:
That retrieves all free 7-day blocks based on the bookings of all properties, but if we need to get the free 7-day blocks for just a particular property, we can use:
Where
<property_id here>is the property_id. We can even do the selection based on multiple properties at a time by simply changing it toWHERE property_id IN (<comma sep'd list of property_ids here>).