web-dev-qa-db-fra.com

Pourquoi CONNECT BY LEVEL sur une table renvoie-t-il des lignes supplémentaires?

L'utilisation de CONNECT BY LEVEL semble renvoyer trop de lignes lorsqu'elle est effectuée sur une table. Quelle est la logique derrière ce qui se passe?

En supposant le tableau suivant:

create table a ( id number );

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

Cette requête renvoie 12 lignes ( SQL Fiddle ).

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

Une ligne pour chacun dans le tableau A avec la valeur de la colonne LVL étant 1 et trois pour chacun dans le tableau A où la colonne LVL est 2, à savoir:

 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 

Elle est équivalente à cette requête, qui renvoie les mêmes résultats.

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

Je ne comprends pas pourquoi ces requêtes renvoient 12 lignes ou pourquoi il y a trois lignes où LVL est 2 et une seule où LVL est 1 pour chaque valeur de la colonne ID.

Augmenter le nombre de niveaux "connectés" à 3 renvoie 13 lignes pour chaque valeur d'ID. 1 où LVL est 1, 3 où LVL est 2 et 9 où LVL est 3. Cela semble suggérer que les lignes retournées sont le nombre de lignes dans le tableau A à la puissance de la valeur de LVL moins 1.

J'aurais cependant pensé que ces requêtes seraient les mêmes que les suivantes, ce qui renvoie 6 lignes

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

Le documentation n'est pas particulièrement clair, pour moi, pour expliquer ce qui devrait se produire. Que se passe-t-il avec ces pouvoirs et pourquoi les deux premières requêtes ne sont-elles pas les mêmes que la troisième?

14
Ben

Dans la première requête, vous vous connectez uniquement au niveau. Donc, si le niveau <= 1, vous obtenez chacun des enregistrements 1 fois. Si niveau <= 2, alors vous obtenez chaque niveau 1 fois (pour le niveau 1) + N fois (où N est le nombre d'enregistrements dans le tableau). C'est comme si vous faisiez des jointures croisées, car vous ne faites que sélectionner tous les enregistrements de la table jusqu'à ce que le niveau soit atteint, sans avoir d'autres conditions pour limiter le résultat. Pour le niveau <= 3, cela se fait à nouveau pour chacun de ces résultats.

Donc pour 3 enregistrements:

  • Niveau 1: 3 record (tous ayant le niveau 1)
  • Niv.2: 3 enregistrements de niveau 1 + 3 * 3 enregistrements de niveau 2 = 12
  • Niv.3: 3 + 3 * 3 + 3 * 3 * 3 = 39 (en effet, 13 enregistrements chacun).
  • Niv 4: commencer à voir un motif? :)

Ce n'est pas vraiment une jointure croisée. Une jointure croisée ne retournerait que les enregistrements qui ont le niveau 2 dans ce résultat de requête, tandis qu'avec cette connexion, vous obtenez les enregistrements ayant le niveau 1 ainsi que les enregistrements ayant le niveau 2, résultant ainsi en 3 + 3 * 3 au lieu de simplement Enregistrement 3 * 3.

14
GolezTrol

Quand connect by est utilisé sans start with clause et prior opérateur, il n'y a aucune restriction sur la jonction d'une ligne enfant à une ligne parent. Et ce que fait Oracle dans cette situation, il renvoie toutes les permutations de hiérarchie possibles en connectant une ligne à chaque ligne de niveau supérieur.

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
14
Nick Krasnov

vous comparez des pommes à des oranges lorsque vous comparez la requête finale aux autres, car le NIVEAU est isolé dans celui de la table double à 1 rangée.

considérons cette requête:

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

ce que cela veut dire, commencez par le jeu de tables (sélectionnez * From a). puis, pour chaque ligne renvoyée, connectez cette ligne à la ligne précédente. comme vous n'avez pas défini de jointure dans la connexion par, il s'agit en fait d'une jointure cartésienne, donc lorsque vous avez 3 lignes de (1,2,3) 1 jointures à 2, 1-> 3, 2-> 1, 2 -> 3, 3-> 1 et 3-> 2 et ils se rejoignent également 1-> 1,2-> 2 et 3-> 3. ces jointures sont de niveau = 2. nous avons donc 9 jointures là-bas, c'est pourquoi vous obtenez 12 lignes (3 lignes originales de "niveau 1" plus l'ensemble cartésien).

donc le nombre de lignes en sortie = rowcount + (rowcount ^ 2)

dans la dernière requête, vous isolez le niveau de cette

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

ce qui bien sûr renvoie 2 lignes. ceci est ensuite cartésien aux 3 lignes d'origine, donnant 6 lignes en sortie.

1
DazzaL

Vous pouvez utiliser la technique ci-dessous pour résoudre ce problème:

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
AlexiWilius