How to Avoid Gaps in Data in MySQL
If you're grouping by time and you don't want gaps in your report data, you need to generate a series of time values and use it to do an outer join with your data. Prior to MySQL 8, you can do this using variables. In the following example we extracted the number of rentals per hour from the Sakila Sample Database:
-- The first line is to make the first value of statement below 0:00:00, not 1:00:00
-- LIMIT 720 gives 30 days worth of hourly values
SET @n:=('2005-05-25' - INTERVAL 1 HOUR);
SELECT
hours.this_hour,
count(rental.rental_id)
FROM
(SELECT (SELECT @n:= @n + INTERVAL 1 HOUR) this_hour
FROM inventory LIMIT 720) hours
LEFT JOIN rental ON (hours.this_hour=date_format(rental.rental_date,'%Y-%m-%d %H:00:00'))
GROUP BY hours.this_hour;
There are serious limitations with the above method. For one thing, you may ask what does the inventory
table have to do with rentals? It does not have anything to do with it directly. The inventory
table got chosen because it has a few thousand rows and we needed one that has at least 720 to generate our series of time values. In reality any table with a large number of rows can be used for this. But what if there is none available?
Fortunately, starting with MySQL 8, you can instead use a common table expression:
WITH RECURSIVE my_hours AS
(
SELECT 0 as inc
UNION ALL
SELECT 1+inc
FROM my_hours WHERE inc<=720
)
SELECT
hours.this_hour,
count(rental.rental_id)
FROM
(SELECT '2005-05-25' + interval inc hour as this_hour
FROM my_hours) as hours
LEFT JOIN rental ON (hours.this_hour=date_format(rental.rental_date,'%Y-%m-%d %H:00:00'))
GROUP BY hours.this_hour;