Você está no caminho certo, apenas
LEFT JOIN
o resultado de sua consulta a uma tabela de nomes de meses. Em seguida, os nomes que existem em seu resultado retornarão uma correspondência e um NULL
será devolvido para aqueles meses que não estão no seu resultado. Um COALESCE
ou NVL
ou CASE
construção, o que você quiser, pode ser usado para transformar esse NULL
em um 0
direto . Você não precisa fazer uma tabela real de meses, você pode obter usando uma visualização em linha - simplesmente uma consulta que gera todos os dados do mês.
select months.month, coalesce(appts.monthly_total, 0) monthly_total
from (
select 'January' as month
union all select 'February'
union all select 'March'
...etc...
union all select 'December'
) months
left join (
select sum(service_price) as monthly_total,
monthname(appt_date_time) as month
from appt_tbl
inner join services_tbl
on appt_tbl.service_id = services_tbl.service_id
where appt_date_time between '2012-01-01 00:00:00'
and '2012-12-31 23:59:59'
group by month(appt_date_time)
) appts
on months.month = appts.month
Quanto ao retorno de tudo para um determinado ano, observe a condição WHERE. Estou assumindo que appt_date_time é um datetime ou timestamp. Você não deseja escrever YEAR(appt_date_time) =2012 porque isso arruinará as chances de usar um índice nessa coluna (suponho que a coluna apareça como a primeira coluna em um índice). Usando um BETWEEN...AND e comparando com datas literais, você pode ter uma consulta bastante eficiente que atende ao requisito.
Além disso, se você
GROUP BY
em month(appt_date_time)
O mysql garantirá que os resultados também sejam classificados em ordem crescente por mês. Portanto, não há necessidade de um ORDER BY
separado . A ordem das 'pernas' da consulta UNION também será preservada pelo mysql.