SQL Query to fill missing gaps across time and get

2019-06-04 23:43发布

问题:

I have the following table in my database:

Month|Year | Value
   1 |2013 | 100
   4 |2013 | 101
   8 |2013 | 102
   2 |2014 | 103
   4 |2014 | 104 

How can I fill in "missing" rows from the data, so that if I query from 2013-03 through 2014-03, I would get:

Month|Year | Value
   3 |2013 | 100
   4 |2013 | 101
   5 |2013 | 101
   6 |2013 | 101
   7 |2013 | 101
   8 |2013 | 102
   9 |2013 | 102
  10 |2013 | 102
  11 |2013 | 102
  12 |2013 | 102
   1 |2014 | 102
   2 |2014 | 103
   3 |2014 | 103

As you can see I want to repeat the previous Value for a missing row.

回答1:

I have created a SQL Fiddle of this solution for you to play with.

Essentially it creates a Work Table @Months and then Cross joins this will all years in your data set. This produces a complete list of all months for all years. I then left join the Test data provided in your example (Table named TEST - see SQL fiddle for schema) back into this list to give me a complete list with Values for the months that have them. The next issue to overcome was using the last months values if this months didn't have any. For that, I used a correlated sub-query i.e. joined tblValues back on itself only where it matched the maximum Rank of a row which has a value. This then gives a complete result set!

If you want to filter by year\month you can add this into a WHERE clause just before the final Order By.

Enjoy!

Test Schema

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)

Query

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