Reputation: 784
I have a table with two columns that contains the start and end of a sequence of integers. It's a large list, but I'm pretty sure there are no overlaps in any of the ranges. I want to generate a list of all numbers contained within these range start and end bounds.
Basically I want to do the opposite of this question: How to create number ranges from a list of numbers?
From what I've read, my best guess at a solution might be some sort of dynamic cross apply, but I'm not really sure how to even begin that.
My table looks something like this:
Table
RangeStart RangeEnd
200 205
208 209
221 221
222 224
I want something like this:
Desired Result
Sequence
200
201
202
203
204
205
208
209
221
222
223
224
Upvotes: 1
Views: 1492
Reputation: 796
You can avoid using a 'numbers' table, by the use of a recursive common table expression:
WITH Numbers AS (
SELECT RangeStart, RangeStart AS Number, RangeEnd from RangeTable
UNION ALL
SELECT RangeStart, Number + 1, RangeEnd FROM Numbers WHERE Number < RangeEnd)
SELECT Number FROM Numbers ORDER BY Number
The first part is the 'anchor' query, which defines the root member (in this case, the range from number, the first number in the range, and the range to number).
The second part (after the UNION ALL
) recursively joins to the anchor member and essentially keeps adding one to Number
and recursing until it hits RangeEnd
.
The final part gets just the numbers from the CTE we have built (we are no longer interested in the range start and end) and makes sure they are in the right order (this might not matter to you, in which case you can omit the ORDER BY
clause.)
edit - if you are hitting a recursion limit with this, you can fix this by adding OPTION (MAXRECURSION 0)
on the end of the query. Apologies for missing this!
Upvotes: 1
Reputation: 7457
declare @Sequence table(
Value int
)
declare @Value int = 0
while (@Value < 500)
begin
insert @Sequence values(@Value)
select @Value += 1
end
select * from @Sequence
declare @Ranges table(
RangeStart int,
RangeEnd int
)
insert into @Ranges values(200, 205)
insert into @Ranges values(208, 209)
insert into @Ranges values(221, 221)
insert into @Ranges values(222, 224)
select s.Value
from @Sequence s
join @Ranges r on r.RangeStart <= s.Value and r.RangeEnd >= s.Value
order by s.Value
The key is getting your list of @Sequence values.
Upvotes: 0
Reputation: 1269623
The easiest way is to start with a list of integers. A convenient one -- if the list is not too long -- is master..spt_values
:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master..spt_values
)
select (rangestart + n.n) as sequence
from ranges r join
n
on r.rangestart + n.n <= r.rangeend
order by sequence;
If you are concerned about overlaps, then the following is an easy way to get the sequence:
select distinct (rangestart + n.n) as sequence
from ranges r join
n
on r.rangestart + n.n <= r.rangeend
order by sequence;
If the problem has lots of overlaps and values, then you would want a somewhat different approach (but that is not the question you are asking).
Upvotes: 0