Fill in missing Date ranges using MySQL

I’m always trying different programming exercises in order to learn and grow as a Developer. One of my favorite learning exercises is porting over from one SQL dialect to another, as they all have their own individual features. Having to hack together or mirror non-existent functionality really challenges my thinking, therefore enabling growth and improvement in my query skills. In this post, I share reproducing the same query results using MySQL for queries I first learned of/discovered that were covered using Oracle SQL and specific implementation features…


Self-Promotion:

If you enjoy the content written here, by all means, share this blog and your favorite post(s) with others who may benefit from or like it as well. Since coffee is my favorite drink, you can even buy me one if you would like!


The Newsletter for PHP and MySQL Developers

Receive a copy of my ebook, “10 MySQL Tips For Everyone”, absolutely free when you subscribe to the OpenLampTech newsletter.


Credit and Sources

I found great inspiration, learning, understanding, and the idea for this blog post based on this YouTube video:

Sample Data

The final query results we want are based on the present data in a table, ‘important_events’ having this fictisious data:

SELECT * FROM important_events;

Notice there are 5 random dates from the same month and year, with an event name and event comment. The goal is to provide results for every day of the entire month, including those days that do not have a row in table ‘important_events’.


Are you a Medium member? If so, receive an email notification each time I publish a blog post if you prefer the Medium platform. Not a member? No worries! Use my sign-up link (I will get a commission at no extra cost to you) and join. I really enjoy reading all the great content there and I know you will too!!!


The final results should be as those shown below:

Information needed

The information we need in order to retrieve the query results are:

  • The first day of the month based on the month value in the column ‘event_date’
  • The last day of the month based on the month value in the column ‘event_date’
  • The total number of days in the month based on the month value in the column ‘event_date’

In determining the first and last day of the respective month, we should use the minimum and maximum ‘event_date’ values currently stored in the ‘important_events’ table. We can easily get these values using both the MIN() and MAX() aggregate functions targeting the ‘event_date’ column:

SELECT MIN(event_date) AS min_date, MAX(event_date) AS max_date
FROM important_events;

Using the minimum and maximum date values, we calculate the first and last day of the month and the number of total days in the target month using specific MySQL Date functions. That query might look as the next example:

SELECT 
DATE_ADD(MIN(event_date), INTERVAL - DAY(MIN(event_date)) + 1 DAY) AS mnth_first_day,
LAST_DAY(MAX(event_date)) AS mnth_last_day,
LAST_DAY(MAX(event_date)) - DATE_ADD(MIN(event_date), INTERVAL - DAY(MIN(event_date)) + 1 DAY) + 1 AS days_diff
FROM important_events;

MySQL does have a native LAST_DAY() date function we use to get the last day of the month. However, there is no FIRST_DAY() date function in MySQL (at the time of writing) so we have to do a little extra work and calculate it ourselves using the expression:

DATE_ADD(MIN(event_date), INTERVAL - DAY(MIN(event_date)) + 1 DAY)

If you would like to support my blog and content, please consider tossing some spare change in my Tip Jar. Thank you so much!


Recursive Common Table Expression

Oracle SQL has a CONNECT BY clause but there is no such native clause like it in MySQL as of the current writing. To replicate this functionality, for the purposes of this particular query, we can use a Recursive Common Table Expression (CTE).

For easier handling, I’ll store the number of days of the month in a session variable using the SET keyword:

SET @num_days := '';

SELECT
LAST_DAY(MAX(event_date)) - DATE_ADD(MIN(event_date), INTERVAL - DAY(MIN(event_date)) + 1 DAY) + 1 INTO @num_days
FROM important_events;

SELECT @num_days;

I can then use the ‘@num_days’ variable in a recursive CTE as shown below:

The ‘n_dte’ CTE returns an incrementing set of INTEGER values equal to the total number of days in the target month (essentially 1 row for each day):

We can now add each individual incrementing INTEGER value to the 1st day of the month date value and return a calendar day for each day of the month. The query to produce this information might look like the below example:

SELECT 
DATE_ADD(MIN(event_date), INTERVAL - DAY(MIN(event_date)) + 1 DAY) AS mnth_first_day,
n,
DATE_ADD(DATE_ADD(MIN(event_date), INTERVAL - DAY(MIN(event_date)) + 1 DAY), INTERVAL n - 1 DAY) AS all_days_in_mnth
FROM important_events
JOIN n_dte
group by n
ORDER BY n ASC;

The Newsletter for PHP and MySQL Developers

Receive a copy of my ebook, “10 MySQL Tips For Everyone”, absolutely free when you subscribe to the OpenLampTech newsletter.


I’ve constrained the output rows for better readability shown in the below screenshot:

Note: Rows output constrained to image size

Multiple Common Table Expressions

We can have multiple CTE’s in the same WITH clause by separating them with commas, so I’ll include this query as its own CTE in addition to the already established, ‘n_dte’:

Together, our 2 CTE’s look like this:

Multiple CTE’s are the foundation of our query.

LEFT JOIN – Final Results Set

Now we simply need to LEFT JOIN the ‘all_mnth_days’ CTE to  the ‘important_events’ table on the 2 date columns:

SELECT
amd.all_days_in_mnth AS event_date, IFNULL(ie.event_name, 'No Event') AS event_name, IFNULL(ie.event_comment, 'No Event Comment') AS event_comment
FROM all_mnth_days AS amd
LEFT JOIN important_events AS ie
ON ie.event_date = amd.all_days_in_mnth;

And we have the desired, final query results:

I won’t lay claim that this is the best, or only, or most performant way to solve this particular query in MySQL. Only that this is how I figured out how to retrieve this particular data. I’d love to know your thoughts, suggestions, and feedback. If you would’ve used a different approach or other MySQL functionality, please share in the comments below so that myself and other readers can learn and broaden our own query chops.

If you have any questions or see any mistakes in the code, please let me know via the comments. Constructive comments help me provide accurate blog posts and are much appreciated. Thank you for reading!


Thank you for reading this post. Please share it with someone else who would enjoy it as well.


Josh Otwell has a passion to grow as a PHP Developer, SQL expert, and technical blogger/writer.

Disclaimer: The majority of examples in this post, are performed in a personal development/learning workstation environment and should not be considered production quality or ready. Your particular goals and needs may vary. Like always, just because you can do something doesn’t mean you should. My opinions are my own.

The Newsletter for PHP and MySQL Developers

Receive a copy of my ebook, “10 MySQL Tips For Everyone”, absolutely free when you subscribe to the OpenLampTech newsletter.

More ways I can help

Disclosure: Some of this blog post’s services and product links are affiliate links. At no additional cost to you, should you make a purchase by clicking through one of them, I will receive a commission.

The Newsletter for PHP and MySQL Developers

Receive a copy of my ebook, “10 MySQL Tips For Everyone”, absolutely free when you subscribe to the OpenLampTech newsletter.

Disclosure: Some of this blog post’s services and product links are affiliate links. At no additional cost to you, should you make a purchase by clicking through one of them, I will receive a commission.

3 thoughts on “Fill in missing Date ranges using MySQL

Hey thanks for commenting! Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.