Eu criei um SQL Fiddle desta solução para você brincar.
Essencialmente, ele cria uma tabela de trabalho @Months e, em seguida, o Cross junta isso a todos os anos em seu conjunto de dados. Isso produz uma lista completa de todos os meses para todos os anos. Em seguida, deixei juntar os dados de teste fornecidos em seu exemplo (tabela chamada TEST - consulte SQL fiddle para esquema) de volta a esta lista para me fornecer uma lista completa com valores para os meses que os possuem. O próximo problema a ser superado foi usar os valores dos últimos meses, se esses meses não tivessem nenhum. Para isso, usei uma subconsulta correlacionada, ou seja, juntei tblValues de volta a si mesma apenas onde correspondia ao Rank máximo de uma linha que possui um valor. Isso fornece um conjunto de resultados completo!
Se você quiser filtrar por ano\mês, você pode adicionar isso em uma cláusula WHERE antes do final Order By.
Apreciar!
Esquema de teste
CREATE TABLE TEST( Month tinyint, Year int, Value int)
INSERT INTO TEST(Month, Year, Value)
VALUES
(1,2013,100),
(4,2013,101),
(8,2013,102),
(2,2014,103),
(4,2014,104)
Consulta
DECLARE @Months Table(Month tinyint)
Insert into @Months(Month)Values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12);
With tblValues as (
select Rank() Over (ORDER BY y.Year, m.Month) as [Rank],
m.Month,
y.Year,
t.Value
from @Months m
CROSS JOIN ( Select Distinct Year from Test ) y
LEFT JOIN Test t on t.Month = m.Month and t.Year = y.Year
)
Select t.Month, t.Year, COALESCE(t.Value, t1.Value) as Value
from tblValues t
left join tblValues t1 on t1.Rank = (
Select Max(tmax.Rank)
From tblValues tmax
Where tmax.Rank < t.Rank AND tmax.Value is not null)
Order by t.Year, t.Month