【问题标题】:Confusion in flow of sql output in oracleoracle中sql输出的流程混乱
【发布时间】:2020-03-13 01:39:07
【问题描述】:

我的表 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的流程?

【问题讨论】:

    标签: sql oracle oracle11g hierarchical-query


    【解决方案1】:

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

    当每行有不同的循环条件时,其结果相乘。

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

    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<>fiddle demo

    干杯!!

    【讨论】:

      【解决方案2】:

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

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

      最好的问候,斯图阿什顿

      【讨论】:

      • 这个级别从 1 到 3 的进度如何?你能解释一下 level
      • 我不明白这 10 行是如何来自第一个示例的?
      • 在我的例子中,我不是从 range_id 到 range_end,而是从 1 到 range_end。由于没有 START WITH 子句,因此两行都是起点。第一行显示一次(级别 1),然后显示第二次(级别 2),因为级别 2 不大于 range_end 2。然后第二行显示,因为级别 3 不大于 range_end 3。之后,第一行输出(级别 1)连接到第二行,因此第二行显示在级别 2。然后该行连接到自身作为级别 3。这使得第一个起点有 5 个输出行。第二个起点的故事相同
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多