I've seen number of posts similar to this, but none quite fit for the scenario I'm working with. Attempting to query a DB and aggregate results for each month in the past 12. If no results exist for a particular month, I would like a 0 in a row for that particular month.
Running this query results in data for Nov 2016 - March 2017, with no results before/after:
SELECT SUM(PRICE) SumPrice, COUNT(*) TotalNum, EXTRACT(MONTH FROM SALE_DATE) Mo, EXTRACT(YEAR FROM SALE_DATE) Yr
FROM SALES_HIST
WHERE SALE_DATE BETWEEN TRUNC(ADD_MONTHS((LAST_DAY(SYSDATE)+1),-12))
AND LAST_DAY(SYSDATE)
AND ITEMNO = 'ABCD'
GROUP BY EXTRACT(MONTH FROM SALE_DATE), EXTRACT(YEAR FROM SALE_DATE)
ORDER BY Yr, Mo;
After some searching, I came up with this query to generate a list of the past 12 months on the fly:
SELECT
EXTRACT(MONTH FROM
ADD_MONTHS(TRUNC(ADD_MONTHS((LAST_DAY(SYSDATE)+1),-12)), LEVEL - 1)) calMo,
EXTRACT(YEAR FROM
ADD_MONTHS(TRUNC(ADD_MONTHS((LAST_DAY(SYSDATE)+1),-12)), LEVEL - 1)) calYr
FROM DUAL
CONNECT BY LEVEL <= 12;
which I believe I should be able to use within my query as a temporary table to fill in the data gaps. This is what I've been tinkering with:
WITH cal AS (
SELECT
EXTRACT(MONTH FROM ADD_MONTHS(TRUNC(ADD_MONTHS((LAST_DAY(SYSDATE)+1),-12)), LEVEL - 1)) calMo,
EXTRACT(YEAR FROM ADD_MONTHS(TRUNC(ADD_MONTHS((LAST_DAY(SYSDATE)+1),-12)), LEVEL - 1)) calYr
FROM DUAL
CONNECT BY LEVEL <= 12
)
select cal.calMo, cal.calYr, NVL(count(*), 0)
from cal
left join SALES_HIST sh on cal.calMo = EXTRACT(MONTH FROM sh.SALE_DATE)
WHERE SALE_DATE BETWEEN TRUNC(ADD_MONTHS((LAST_DAY(SYSDATE)+1),-12)) AND LAST_DAY(SYSDATE)
AND ITEMNO = 'ABCD'
group by cal.calMo, cal.calYr
order by cal.calYr, cal.calMo;
but this seems to return the same results as the initial query. I assume I'm missing something with the join, but not sure exactly. Any help would be greatly appreciated. Thanks!