假设我有两行,我想将它们都展开,以便为范围内的每个整数获取一行。例如,以这张表为例:
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
如果我尝试类似您的尝试:
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
这是什么乱七八糟的?看起来我从每一行开始并连接到另一行 -这是有道理的,因为我并不是说要留在同一行。让我们再试一次:
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
现在我参考了之前的东西——range_id。Oracle 看到同一个 range_id 连续两次被访问,因此它假定存在无限循环并中止执行。
有一种方法可以避免该错误,使用 NOCYCLE 关键字:
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
好吧,我没有得到错误,但是 Oracle 仍然认为两次执行相同的 range_id 会是一个循环,所以它首先停止。
我们需要的是在前一行添加一些东西,让 Oracle 认为它是不同的。SYS_GUID() 是一个成本非常低的函数,它返回一个非重复值。如果我们在条件中引用 PRIOR SYS-GUID(),这足以使前一行唯一并防止无限循环的感知。
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
将此技术应用于您的数据:
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;
另一个答案也有效!我只是想解释一下 CONNECT BY 的工作原理。
最好的问候,炖阿什顿