Reputation: 4330
This is a question on filing missing ranges of data. Specifically, I have a result set where each row contains a StartDate and EndDate value. Let's say I have:
Start End
1/15 1/20
1/12 3/15
I would need a query that produces are adds the following row to the data:
1/21 2/11
Most other related questions are about filling gaps from a know set (like a list of dates). I this case I'm just looking for the start/end of the missing data.
Upvotes: 0
Views: 133
Reputation: 8208
Assuming that you meant to write 1/21 instead of 2/21, here's a way to do it:
with dates as
(
select '2016-01-15' as dtStart, '2016-01-20' as dtEnd union all
select '2016-02-12', '2016-03-15' union all
select '2016-03-21', '2016-04-11'
),
calcs as
(
select
dateadd(day, 1, dtEnd) as rangeStart,
(select dateadd(day, -1, min(dtStart))
from dates d2 where d2.dtStart > d.dtEnd) as rangeEnd
from dates d
)
select *
from calcs c
where c.rangeEnd >= c.rangeStart
The table dates
is just three rows of sample dates. In thecalcs
table, the rangeStart
column is the next day after each dtEnd
. The rangeEnd
column takes the next dtStart
and subtracts a day. Finally, the last row will have a null because there is now missing range after the last dtEnd, so I ignore the row with the null rangeEnd
value.
Edit: If you're unfamiliar with the with
statement in my code, it's a CTE. I used it here as a quick way to create a table with some sample data in it (dates) and a place to store calculations (calcs).
Edit2: Since you mentioned using a join in your comment, here's a way to do that:
with dates as
(
select '2016-01-15' as dtStart, '2016-01-20' as dtEnd union all
select '2016-02-12', '2016-03-15' union all
select '2016-03-21', '2016-04-11'
),
calcs as
(
select
dateadd(day, 1, d1.dtEnd) as rangeStart,
dateadd(day, -1, min(d2.dtStart)) as rangeEnd
from dates d1
join dates d2 on d1.dtEnd < d2.dtStart
group by d1.dtEnd
)
select *
from calcs
where datediff(day, rangeStart, rangeEnd) >= 0
Edit3: updated the datediff inequality to include ranges of one day
Upvotes: 2
Reputation: 81950
Declare @Table table (Start Date,[End] Date)
Insert into @Table values
('2016-01-15','2016-01-20'),
('2016-02-12','2016-03-15')
;with cteBase as (
Select *,Gap=DateDiff(DD,Lag([End],1,Start) over (Order By Start),Start) From @Table
)
Select Start=DateAdd(DD,1+Gap*-1,Start),[End]=DateAdd(DD,-1,Start) from cteBase Where Gap<>0
Returns
Start End
2016-01-21 2016-02-11
Optional for full dataset
...
Union All
Select Start,[End] from cteBase
Order by Start
Upvotes: 0
Reputation: 3701
select * from
(select DISTINCT t1.[end] + 1 'start', t2.start - 1 'end'
from yourtable t1
CROSS JOIN
yourtable t2
WHERE
EXISTS(SELECT 0 FROM yourtable t3 WHERE t3.[end] < t2.[start])
AND
NOT EXISTS(SELECT 0 FROM yourtable t4 WHERE t4.start = t1.[end] + 1)
AND
NOT EXISTS(SELECT 0 FROM yourtable t5 WHERE T5.Start BETWEEN t1.[end] + 1 AND t2.start - 1 OR T5.[end] BETWEEN t1.[end] + 1 AND t2.start - 1)
AND
t1.[end] + 1 <= t2.start - 1
UNION
SELECT start,[end] from yourtable) dq
order by start
Upvotes: 0
Reputation: 2507
I would recommend creating a date table. This will keep your query simple and you can reuse in other queries. Below is a link to an article on creating a date table. Hope this helps.
Upvotes: 0