7

Can you please let me know the SQL to split date ranges when they overlap?

Data (sample data with a date range and possibly other columns):

    Col1 FromDate ToDate
 1. 1    1/1/2008 31/12/2010
 2. 1    1/1/2009 31/12/2012
 3. 1    1/1/2009 31/12/2014

Output:

   Col1  From Date ToDate
1. 1     1/1/2008 31/12/2008 (from row 1 above)
2. 1     1/1/2009 31/12/2010 (from rows 1,2 and 3 above)
3. 1     1/1/2011 31/12/2012 (from rows 2 and 3 above)
4. 1     1/1/2013 31/12/2014 (from row 3 above)
casperOne
  • 73,706
  • 19
  • 184
  • 253
user26309
  • 101
  • 1
  • 5
  • Ok, I don't have the complete answer, but here are some points to consider. 1. The first block of time is select min(FromDate) from table group by FromDate`print("code sample");` 2. Then end of the first block is select min(FromDate) from table group by FromDate where FromDate > "start of block" 3. Repeat as needed. :) – Craig Oct 08 '08 at 22:23

2 Answers2

6

This should do the trick (MySQL dialect, but easily adaptable)

Initial setup

SQL query: SELECT * FROM `test` LIMIT 0, 30 ;
Rows: 3
start       end
2008-01-01  2010-12-31
2009-01-01  2012-12-31
2009-01-01  2014-12-31

Query

SELECT 
  `start` , min( `end` )
FROM (
  SELECT t1.start, t2.end
  FROM test t1, test t2
  WHERE t1.start < t2.end
  UNION
  SELECT t1.end + INTERVAL 1 DAY , t2.end
  FROM test t1, test t2
  WHERE t1.end + INTERVAL 1 DAY < t2.end
  UNION
  SELECT t1.start, t2.start - INTERVAL 1 DAY
  FROM test t1, test t2
  WHERE t1.start < t2.start - INTERVAL 1 DAY
) allRanges
GROUP BY `start`

Result

start       min( `end` )
2008-01-01  2008-12-31
2009-01-01  2010-12-31
2011-01-01  2012-12-31
2013-01-01  2014-12-31
casperOne
  • 73,706
  • 19
  • 184
  • 253
Sklivvz
  • 30,601
  • 24
  • 116
  • 172
2

Skliwz's answer adapted for SQL Server:

DECLARE @DateTest TABLE 
(
    FromDate datetime,
    ToDate datetime 
)

insert into @DateTest (FromDate, ToDate)
(
select cast('1/1/2008' as datetime), cast('12/31/2010' as datetime)
union
select cast('1/1/2009' as datetime), cast('12/31/2012' as datetime)
union
select cast('1/1/2009' as datetime), cast('12/31/2014' as datetime)
)

SELECT 
  FromDate , min(ToDate)
FROM (
  SELECT t1.FromDate, t2.ToDate
  FROM 
    @DateTest t1, 
    @DateTest t2
  WHERE t1.FromDate < t2.ToDate

  UNION

  SELECT dateadd(DAY, 1, t1.ToDate), t2.ToDate
  FROM 
    @DateTest t1, 
    @DateTest t2
  WHERE dateadd(DAY, 1, t1.ToDate) < t2.ToDate
) allRanges
group by FromDate
Even Mien
  • 44,393
  • 43
  • 115
  • 119