If you simply extract the start and end position of each item, for which you could use something like:
DECLARE @Value VARCHAR(20) = '2-6';
SELECT Start = TRY_CONVERT(INT, LEFT(@Value, CHARINDEX('-', @Value) - 1)),
[End] = TRY_CONVERT(INT, SUBSTRING(@Value, CHARINDEX('-', @Value) + 1, LEN(@Value)));
Which gives:
Start End
------------
2 6
Then you can simply join to master..spt_values
:
DECLARE @range VARCHAR(50) = '2-6,9-12,15-20'
SELECT s.Value, v.Number
FROM STRING_SPLIT(@range,',') AS s
INNER JOIN master..spt_values AS v
ON v.Number >= TRY_CONVERT(INT, LEFT(s.Value, CHARINDEX('-', s.Value) - 1))
AND v.Number <= TRY_CONVERT(INT, SUBSTRING(s.Value, CHARINDEX('-', s.Value) + 1, LEN(s.Value)))
AND v.Type = 'P';
For what it is worth master..spt_values
is fairly limited, there are better ways to generate a set or sequence without a loop.
When using master..spt_values
the range 2500-2501
wouldn't work, but if you had a numbers table, or a different source of numbers it would be fine, e.g. the following works up to 10,000, then by uncommenting further lines you can increase the range:
DECLARE @range VARCHAR(50) = '2-6,9-12,15-20,2500-2501';
WITH Numbers (Number) AS
( SELECT ROW_NUMBER() OVER(ORDER BY N1.N)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N1 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N2 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N3 (N) -- 1,000
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N4 (N) -- 10,000
--CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N5 (N) -- 100,000
)
SELECT s.Value, n.Number
FROM STRING_SPLIT(@range,',') AS s
INNER JOIN Numbers AS n
ON n.Number >= TRY_CONVERT(INT, LEFT(s.Value, CHARINDEX('-', s.Value) - 1))
AND n.Number <= TRY_CONVERT(INT, SUBSTRING(s.Value, CHARINDEX('-', s.Value) + 1, LEN(s.Value)))
ORDER BY n.Number;