Du är på rätt väg, bara LEFT JOIN
resultatet av din fråga till en tabell med månadsnamn. Då kommer namnen som finns i ditt resultat att returnera en matchning och en NULL
kommer att returneras för de månader som inte finns i ditt resultat. En COALESCE
eller NVL
eller CASE
konstruktion, vad du än vill, kan användas för att vända den NULL
till en rak 0
.
Du behöver inte göra en riktig tabell över månader, du kan få genom att använda en inlinevy - helt enkelt en fråga som genererar all månadsdata.
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
När det gäller att returnera allt för ett visst år, titta på WHERE-villkoret. Jag antar att appt_date_time är en datetime eller tidsstämpel. Du vill inte skriva YEAR(appt_date_time) =2012 eftersom det kommer att förstöra chanserna att använda ett index på den kolumnen (jag antar att kolumnen visas som en första kolumn i ett index). Genom att använda en BETWEEN...AND och jämföra med bokstavliga datumtider kan du få en ganska effektiv fråga som uppfyller kravet.
Dessutom, om du GROUP BY
på month(appt_date_time)
mysql kommer att säkerställa att resultaten sorteras stigande efter månad också. Så inget behov av en separat ORDER BY
. Ordningen på "benen" i UNION-frågan kommer också att bevaras av mysql.