在表上执行时,使用CONNECT BY LEVEL似乎返回太多行。正在发生的事情背后的逻辑是什么?
假设下表:
create table a ( id number );
insert into a values (1);
insert into a values (2);
insert into a values (3);
此查询返回12行(SQL Fiddle)。
select id, level as lvl
from a
connect by level <= 2
order by id, level
表A中每行一行,列LVL的值为1,表LV中的每一行为3,其中列LVL为2,即:
ID | LVL ---+----- 1 | 1 1 | 2 1 | 2 1 | 2 2 | 1 2 | 2 2 | 2 2 | 2 3 | 1 3 | 2 3 | 2 3 | 2
它等效于此查询,返回相同的结果。
select id, level as lvl
from dual
cross join a
connect by level <= 2
order by id, level
我不明白为什么这些查询会返回12行,或者为什么有三行LVL为2,而对于ID列的每个值,只有一行LVL为1。
为每个ID值增加“连接”到3 returns 13 rows的级别数。 1其中LVL是1,其中LVL是2和9,其中LVL是3.这似乎表明返回的行是表A中的行数,LVL的值减去1的幂。
我希望这些查询与下面的查询相同,返回 6行
select id, lvl
from ( select level as lvl
from dual
connect by level <= 2
)
cross join a
order by id, lvl
在解释应该发生什么时,documentation对我来说并不是特别清楚。这些权力发生了什么,为什么不是前两个查询与第三个相同?
答案 0 :(得分:14)
如果在没有connect by
子句和start with
运算符的情况下使用prior
,则将子行连接到父行没有限制。在这种情况下,Oracle会做什么,它通过将行连接到更高级别的每一行来返回所有可能的层次结构排列。
SQL> select b
2 , level as lvl
3 , sys_connect_by_path(b, '->') as ph
4 from a
5 connect by level <= 2
6 ;
B LVL PH
---------- ----------
1 1 ->1
1 2 ->1->1
2 2 ->1->2
3 2 ->1->3
2 1 ->2
1 2 ->2->1
2 2 ->2->2
3 2 ->2->3
3 1 ->3
1 2 ->3->1
2 2 ->3->2
3 2 ->3->3
12 rows selected
答案 1 :(得分:12)
在第一个查询中,只按级别连接。 因此,如果级别&lt; = 1,您将获得每个记录1次。如果级别&lt; = 2,那么您将获得每个级别1次(级别1)+ N次(其中N是表中的记录数)。这就像你是交叉加入一样,因为你只是从表中选择所有记录直到达到等级,而没有其他条件来限制结果。对于等级&lt; = 3,对于每个结果再次完成。
因此有3条记录:
这不是真正的交叉联接。交叉连接只会返回在此查询结果中具有级别2的记录,而使用此连接时,您将获得级别为1的记录以及级别为2的记录,从而导致3 + 3 * 3而不是仅3 * 3记录。
答案 2 :(得分:1)
在将最终查询与其他查询进行比较时,您将苹果与橙子进行比较,因为LEVEL被隔离到1行双表中。
让我们考虑一下这个问题:
select id, level as lvl
from a
connect by level <= 2
order by id, level
这就是说,从表集开始(select * From a)。然后,对于返回的每一行,将此行连接到前一行。因为你没有在连接中定义一个连接,这实际上是一个笛卡尔连接,所以当你有3行(1,2,3)1个连接到2时,1&gt; 3,2-> 1 ,2-> 3,3-> 1和3-> 2,它们也与它们自身连接1-> 1,2-> 2和3-> 3。这些连接是level = 2。所以我们有9个连接,这就是为什么你得到12行(3个原始的“1级”行加上笛卡尔集)。
所以输出的行数= rowcount +(rowcount ^ 2)
在上一个查询中,您将级别隔离到此
select level as lvl
from dual
connect by level <= 2
当然返回2行。然后将其发送到原始的3行,输出为6行。
答案 3 :(得分:0)
您可以使用以下技术来解决此问题:
select id, level as lvl
from a
left outer join (select level l from dual connect by level <= 2) lev on 1 = 1
order by id