Você normalmente consegue esse tipo de coisa juntando-se a uma tabela de valores , ou seja, uma tabela que contém todos os valores nos quais você está interessado.
As tabelas de valores típicas podem conter, por exemplo, todos os valores inteiros entre 0 e 1.000, ou todas as datas de um determinado período. Muitas vezes, as tabelas Value incluem mais valores do que o desejado e obtemos a saída exata desejada adicionando filtros na cláusula WHERE.
Nesse caso, você precisará de uma tabela que contenha datas. Supondo que essa tabela seja denominada ValTableDates e que contenha todas as datas entre janeiro de 2005 e dezembro de 2010, a consulta ficaria assim:
SELECT AVG(data) AS data, VT.ValDate
FROM ValTableDates VT
LEFT JOIN table T ON T.dateReg = VT.ValDate
WHERE VT.ValDate > [Some Start Date] and VT < [Some End Date]
GROUP BY YEAR(dateReg), MONTH(dateReg), DAY(dateReg)
ORDER BY dateReg
A consulta acima pode exigir um pouco de ajuste para obter um valor Zero em vez de NULL, mas o ponto principal é que a Tabela de Valores é normalmente a maneira mais simples de fornecer registros de saída para pontos de dados ausentes.
Uma alternativa é usar uma função/expressão que produz a sequência [data] desejada, dentro de uma subconsulta, mas geralmente é menos eficiente e mais propensa a erros.