Suppose I have two rows and I want to expand both of them to get one row for every integer in the range. For example, take this table:
drop table U purge;
create table U as
select 1 range_id, 2 range_end from dual
union all
select 2, 3 from dual;
select * from u;
ID END
1 2
2 3
If I try something like your attempt:
select range_id, range_end, level
from u
connect by level <= range_end;
ID END LEVEL
1 2 1
1 2 2
2 3 3
2 3 2
2 3 3
2 3 1
1 2 2
2 3 3
2 3 2
2 3 3
What is this mess? It looks like I’m starting with each row and connecting to the other row – which makes sense because I’m not saying to stay on the same row. Let’s try again:
select range_id, range_end, level
from u
connect by level <= range_end
and range_id = prior range_id
Error report - SQL Error: ORA-01436: CONNECT BY loop in user data
Now I made a reference to something PRIOR – the range_id. Oracle sees that the same range_id is accessed twice in a row, so it assumes there is an infinite loop and aborts the execution.
There is a way to avoid that error, using the NOCYCLE keyword:
select range_id, range_end, level
from u
connect by nocycle level <= range_end
and range_id = prior range_id;
ID END LEVEL
1 2 1
2 3 1
Well, I didn’t get the error, but Oracle still considers that doing the same range_id twice would be a loop, so it stops first.
What we need is to add something to the prior row that will make Oracle think it is different. SYS_GUID() is a very low-cost function that returns a nonrepeating value. If we refer to PRIOR SYS-GUID() in a condition, that is enough to make the prior row unique and to prevent the perception of an infinite loop.
select range_id, range_end, level
from u
connect by level <= range_end
and range_id = prior range_id
and prior sys_guid() is not null;
ID END LEVEL
1 2 1
1 2 2
2 3 1
2 3 2
2 3 3
Applying this technique to your data:
with data(start_range, end_range) as (
select 1, 3 from dual
union all select 5, 7 from dual
)
SELECT start_range, end_range,
start_range + level - 1 num
FROM data
CONNECT BY start_range + level - 1 <= END_RANGE
and start_range = prior start_range
and prior sys_guid() is not null;
The other answer works too! I am just trying to explain how CONNECT BY works.
Best regards, Stew Ashton