Почему CONNECT BY LEVEL в таблице возвращает дополнительные строки?

использование 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, т. е.:

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 и только один, где LVL равен 1 для каждого значения столбца ID.

увеличение количества уровней, которые "подключены" к 3 возвращает 13 строк для каждого значения ID. 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 ответов


в первом запросе вы подключаетесь только по уровню. Поэтому, если уровень

Так для 3 записи:

  • 1 лвл: 3 записи (все, имеющие 1 уровень)
  • LVL 2: 3 записи, имеющие уровень 1 + 3*3 записи, имеющие уровень 2 = 12
  • 3 лвл: 3 + 3*3 + 3*3*3 = 39 (действительно, 13 записей в каждой).
  • Lvl 4: начало видеть шаблон? :)

на самом деле это не перекрестное соединение. Перекрестное соединение возвращает только те записи, которые имеют уровень 2 в этом результате запроса, в то время как с этим connect by вы получаете записи, имеющие уровень 1, а также записи, имеющие уровень 2, в результате чего 3 + 3*3 вместо того, чтобы просто 3 * 3 запись.


, когда 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-строчной двойной таблицей.

рассмотрим этот запрос:

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

что это значит, начните с набора таблиц (выберите * из a). затем для каждой возвращенной строки подключите эту строку к предыдущей строке. поскольку вы не определили соединение в connect by, это фактически Декартовое соединение, поэтому, когда у вас есть 3 строки (1,2,3) 1, присоединяется к 2, 1->3, 2->1, 2 - >3, 3->1 и 3->2 и они также присоединяются к себе 1->1,2->2 и 3 - >3. эти соединения имеют уровень=2. таким образом, у нас есть 9 соединений, поэтому вы получаете 12 строк (3 исходных строки "уровня 1" плюс Декартовый набор).

таким образом, количество выходных строк = rowcount + (rowcount^2)

в последнем запросе вы изолируете уровень

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

что, конечно, возвращает 2 строки. это затем cartesianed в исходные 3 строки, давая 6 строк в качестве вывода.


вы можете использовать технику, чтобы преодолеть эту проблему:

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