1

我有表 T 为:

在此处输入图像描述

所以,我从某个地方得到了 sql:

select * from (
SELECT start_range,(LEVEL + START_RANGE)-1 NUM,end_range
   FROM offc.T
  CONNECT BY (LEVEL +START_RANGE ) <= END_RANGE+1) order by start_range,num,end_range;

我得到的输出为:

在此处输入图像描述

我正在描述这个查询现在是如何运行的:

在第一级=1 所以,start_range=1 和 end_range=3,它循环到 1 到 3;所以,输出是:

start_range | num | end_range
   1          1       3       2<=4,level=1
   1           2       3      3<=4,level=2
   1           3       3      4<=4,level=3
and the new start_range=5 and end_range=5.

但是,我很困惑这个循环是怎么回事?我看到大量没有 1 2 3 数据的行:

在此处输入图像描述

这 1 2 3 行如何不止一次出现?谁能帮我理解这个sql的流程?

4

2 回答 2

3

假设我有两行,我想将它们都展开,以便为范围内的每个整数获取一行。例如,以这张表为例:

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 的工作原理。

最好的问候,炖阿什顿

于 2019-11-17T16:27:31.410 回答
0

通过查询连接并不像您想象的那样工作。

当每行有不同的循环条件时,其结果会成倍增加。

您可以使用以下查询来获得所需的结果:

with t (start_range, end_range) as
(select 1,3 from dual union all
select 5,5 from dual union all
select 7,9 from dual)
select start_range, end_range, start_range+a.column_value as num
from t, table(cast(multiset(
                 select level-1 from dual
                 connect by level <= end_range - start_range + 1 ) 
           as sys.odciNumberList)) a

db<>小提琴演示

干杯!!

于 2019-11-17T16:10:02.787 回答