Let say I have these two tables in mysql.
table1:
date staff_no
2016-06-10 1
2016-06-09 1
2016-05-09 1
2016-04-09 1
You can build an inline set of variables representing all the dates you want by using any other table in your system that has AT LEAST the number of months you are trying to represent even though the data does not have to have dates. Just has records that you can put a limit on.
TRY the following statement that uses MySql variables. The FROM clause declares a variable inline to the SQL statement "@Date1". I am starting it with MARCH 1 of 2016. Now, the select fields list takes that variable and keeps adding 1 month at a time to it. Since it is combined with the "AnyTableWithAtLeast12Records" (literally any table in your system with at least X records), it will create a result showing the dates. This is one way of forcing a calendar type of list.
But notice the SECOND column in this select does not change the @Date1 via the := assignment. So, it takes the date as it now stands and adds another month to it for the END Date. If you need a smaller or larger date range, just change the limit of records to create the calendar spread...
select
@Date1 := date_add( @Date1, interval 1 month ) StartDate,
date_add( @Date1, interval 1 month ) EndDate
from
AnyTableWithAtLeast12Records,
( select @Date1 := '2016-03-01' ) sqlvars
limit 12;
The result is something like...
StartDate EndDate
2016-04-01 2016-05-01
2016-05-01 2016-06-01
2016-06-01 2016-07-01
2016-07-01 2016-08-01
2016-08-01 2016-09-01
2016-09-01 2016-10-01
2016-10-01 2016-11-01
2016-11-01 2016-12-01
2016-12-01 2017-01-01
2017-01-01 2017-02-01
2017-02-01 2017-03-01
2017-03-01 2017-04-01
Now you have your dynamic "Calendar" completed in one simple query. Now, use that as a basis for all the records you need counts for and format as you had. So take the entire query above as a JOIN to find records within those date ranges... No other queries or stored procedures required. Now, a simple LEFT JOIN will keep all dates, but only show those with staff when WITHIN the between range of per start/end. So ex: greater or equal to 04/01/2016, but LESS THEN 05/01/2016 which includes 04/30/2016 @ 11:59:59pm.
SELECT
DATE_FORMAT(MyCalendar.StartDate,'%b %Y') as month,
COALESCE(COUNT(T1.Staff_no),0) as total_records,
COALESCE(T2.name,"") as name
FROM
( select @Date1 := date_add( @Date1, interval 1 month ) StartDate,
date_add( @Date1, interval 1 month ) EndDate
from
AnyTableWithAtLeast12Records,
( select @Date1 := '2016-03-01' ) sqlvars
limit 12 ) MyCalendar
LEFT JOIN table1 T1
ON T1.Date >= MyCalendar.StartDate
AND T1.Date < MyCalendar.EndDate
AND T1.Staff_No = 1
LEFT JOIN table2 T2
ON T1.staff_no = T2.StaffNo
GROUP BY
T2.name,
DATE_FORMAT(MyCalendar.StartDate,'%Y-%m')
ORDER BY
DATE_FORMAT(MyCalendar.StartDate,'%Y-%m-%d')