• Home
  • About
  • Resume

TSQL: Filling missing date range

By , 08/29/2011 4:47 PM

Let’s say you have a table with some dates and numeric values e.g.:

2011-08-19	16
2011-08-22	45
2011-08-24	62
2011-08-25	88
2011-08-27	17
2011-08-28	35
2011-09-01	10
2011-09-02	79
2011-09-03	70
2011-09-07	83

As you can see not all dates are in the sequential order, 24th comes after 22nd etc. But in many cases you need this data to be sequential, for example if this data feeds a chart you need to fill it with missing dates and 0 for a value. There is an easy way to achieve this.

Some solution suggest creation of a Calendar table – table with prefilled dates and values, but for this task it would be an overkill. A small Common Table Expression will suffice instead.

Let’s say your table with data is called #TEMP_DATA, column with dates is called MyDate, column with integer values is called MyValue. Consider following CTE:

-- Getting date limits
DECLARE @MaxDate DateTime
DECLARE @MinDate DateTime

SELECT @MaxDate = MAX(MyDate), @MinDate = MIN(MyDate) FROM #TEMP_DATA;

-- Calendar CTE with ALL the dates
WITH mycte AS
    (
        SELECT @MinDate AS DateValue
        UNION ALL
        SELECT DateValue + 1
        FROM mycte   
        WHERE DateValue + 1 <= @MaxDate
    )
SELECT DateValue, 0 AS IntValue
FROM mycte 
OPTION (MAXRECURSION 0)

It will produce full date range within limits specified by dates in #TEMP_DATA table:

2011-08-19 	0
2011-08-20 	0
2011-08-21 	0
2011-08-22 	0
2011-08-23 	0
2011-08-24 	0
2011-08-25 	0
2011-08-26 	0
2011-08-27 	0
2011-08-28 	0
2011-08-29 	0
2011-08-30 	0
2011-08-31 	0
2011-09-01 	0
2011-09-02 	0
2011-09-03 	0
2011-09-04 	0
2011-09-05 	0
2011-09-06 	0
2011-09-07 	0

Only thing remains is to marry it in happy UNION to original table with data:

DECLARE @MaxDate DateTime
DECLARE @MinDate DateTime

SELECT @MaxDate = MAX(MyDate), @MinDate = MIN(MyDate) FROM #TEMP_DATA;

-- Calendar CTE with ALL the dates
WITH mycte as
    (
        SELECT @MinDate AS DateValue
        UNION ALL
        SELECT DateValue + 1
        FROM mycte   
        WHERE DateValue + 1 <= @MaxDate
    )
-- Original data        
SELECT MyDate, MyValue FROM #TEMP_DATA 
UNION
-- Filled in data
SELECT DateValue, 0
FROM mycte LEFT JOIN #TEMP_DATA ON DateValue = MyDate WHERE MyDate IS NULL
ORDER BY 1
OPTION (MAXRECURSION 0)

Query selects original data that has dates missing and then UNIONs it with full date range from the CTE which is limited only to missing dates by LEFT JOIN. The result is perfect combination:

2011-08-19	16
2011-08-20	0
2011-08-21	0
2011-08-22	45
2011-08-23	0
2011-08-24	62
2011-08-25	88
2011-08-26	0
2011-08-27	17
2011-08-28	35
2011-08-29	0
2011-08-30	0
2011-08-31	0
2011-09-01	10
2011-09-02	79
2011-09-03	70
2011-09-04	0
2011-09-05	0
2011-09-06	0
2011-09-07	83

All the dates are in sequential order, original date range is preserved and missing values are filled with zeroes.

Leave a Reply

Panorama Theme by Themocracy