14

在表上执行时,使用 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,表 A 中 LVL 列为 2 的每一行,即:

身份证 | 层积层
--+-----
 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 行,或者为什么对于 ID 列的每个值,LVL 为 2 的行有 3 行,而 LVL 为 1 的行只有 1 行。

将“连接”的级别数增加到 3 会为每个 ID 值返回 13 行。1,其中 LVL 为 1,3,其中 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

对我来说,文档在解释应该发生的事情时并不是特别清楚。这些权力发生了什么?为什么前两个查询与第三个查询不同?

4

4 回答 4

16

在第一个查询中,您仅按级别进行连接。因此,如果 level <= 1,您将获得每条记录 1 次。如果级别 <= 2,那么您将获得每个级别 1 次(对于级别 1)+ N 次(其中 N 是表中的记录数)。这就像您在交叉连接,因为您只是从表中挑选所有记录直到达到级别,而没有其他条件来限制结果。对于级别 <= 3,对每个结果再次执行此操作。

所以对于3条记录:

  • Lvl 1:3 条记录(均具有 1 级)
  • Lvl 2:3 条 1 级记录 + 3*3 3 条 2 级记录 = 12
  • Lvl 3:3 + 3*3 + 3*3*3 = 39(实际上,每个 13 条记录)。
  • Lvl 4:开始看到模式?:)

这不是真正的交叉连接。交叉连接只会返回此查询结果中具有级别 2 的那些记录,而通过此连接方式,您将获得具有级别 1 的记录以及具有级别 2 的记录,从而导致 3 + 3*3 而不仅仅是3*3 记录。

于 2012-11-24T12:35:59.170 回答
16

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
于 2012-11-24T12:36:43.573 回答
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->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 行作为输出。

于 2012-11-24T12:32:56.630 回答
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
于 2016-01-09T08:43:46.463 回答