Porque é que o CONNECT por nível numa tabela devolve linhas extra?

usar o CONNECT por nível parece devolver demasiadas linhas quando executadas numa tabela. Qual é a lógica por trás do que está acontecendo?

assumindo o seguinte quadro:

create table a ( id number );

insert into a values (1);
insert into a values (2);
insert into a values (3);

esta pesquisa devolve 12 linhas (SQL Fiddle).

 select id, level as lvl
   from a
connect by level <= 2
  order by id, level

uma linha para cada uma no quadro A, sendo o valor da coluna LVL 1 e três para cada uma no quadro A, sendo a coluna LVL 2, ou seja:

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 

é equivalente a esta consulta, que retorna os mesmos resultados.

 select id, level as lvl
   from dual
  cross join a
connect by level <= 2
  order by id, level

não entendo por que essas consultas retornam 12 linhas ou por que existem três linhas onde o LVL é 2 e apenas uma onde o LVL é 1 para cada valor da coluna ID.

O aumento do número de níveis que estão "ligados" a 3 devolve 13 linhas para cada valor de ID. 1 em que a LVL é de 1, 3 em que a LVL é de 2 e 9 em que a LVL é de 3. Isto parece sugerir que as linhas devolvidas são o número de linhas na tabela a para a potência do valor de LVL menos 1.

Eu teria pensado que estes as consultas seriam as mesmas que as seguintes, que retornam 6 linhas

select id, lvl
  from ( select level  as lvl
           from dual
        connect by level  <= 2
                )
 cross join a
 order by id, lvl

a documentação não é particularmente clara, para mim, para explicar o que deve acontecer. O que se passa com estes poderes e porque é que as duas primeiras perguntas não são as mesmas que a terceira?

Author: Ben, 2012-11-24

4 answers

Na primeira consulta, você se conecta apenas pelo nível. Então, se Nível Então para 3 registos:

  • Lvl 1: 3 registo (todos com nível 1)
  • registos Lvl 2: 3 com nível 1 + 3*3 registos de Nível 2 = 12
  • Lvl 3: 3 + 3*3 + 3*3*3 = 39 (na verdade, 13 registros cada).
  • Lvl 4: a começar a ver um padrão? :)
Não é uma junção cruzada. Uma junção cruzada só retornaria os registros que têm o Nível 2 Neste resultado da consulta, enquanto com esta conexão por, você obtém os registros que têm o Nível 1, bem como os registros que têm o Nível 2, assim resultando em 3 + 3*3 em vez de apenas 3 * 3.
 11
Author: GolezTrol, 2012-11-24 12:41:28

Quando connect by é usado sem a cláusula start with e prior operador, não há restrições em juntar a linha de filhos a uma linha de origem. E o que a Oracle faz nesta situação, ela retorna todas as possíveis permutações de hierarquia, conectando uma linha a cada linha de nível mais alto.

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
 12
Author: Nick Krasnov, 2012-11-24 12:36:43

Você está comparando maçãs com laranjas ao comparar a consulta final com as outras, como o nível é isolado na tabela dual de 1 linha.

Vamos considerar esta consulta:

 select id, level as lvl
   from a
connect by level <= 2
  order by id, level

O que isso quer dizer é, Comece com o conjunto da tabela (selecione * de a). então, para cada linha retornada conecte esta linha à linha anterior. como você não definiu uma junção na conexão por, isso é de fato uma junção cartesiana, então quando você tem 3 linhas de (1,2,3) 1 se junta a 2, 1->3, 2->1, 2->3, 3->1 e 3- > 2 e associam - se também a si próprios 1->1,2->2 e 3->3. Estas juntas são de Nível=2. então temos 9 juntas lá, e é por isso que você tem 12 linhas (3 linhas originais "nível 1" Mais o conjunto cartesiano).

De modo que o número de linhas de saída = número de linhas + (número de linhas^2)

Na última consulta você está isolando o nível para este

select level  as lvl
           from dual
        connect by level  <= 2

O qual, obviamente, devolve 2 linhas. este é então cartesimado para as 3 linhas originais, dando 6 linhas como saída.

 1
Author: DazzaL, 2012-11-24 12:32:56

Pode usar a técnica abaixo para ultrapassar este problema:

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
 0
Author: AlexiWilius, 2016-01-09 08:43:46