Почему 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

Документация не особенно понятна для меня, объясняя, что должно произойти. Что происходит с этими полномочиями и почему не первые два вопроса совпадают с третьим?

Ответ 1

В первом запросе вы подключаетесь только уровнем. Поэтому, если уровень <= 1, вы получаете каждую из записей 1 раз. Если уровень <= 2, вы получаете каждый уровень 1 раз (для уровня 1) + N раз (где N - количество записей в таблице). Это похоже на перекрестное соединение, потому что вы просто выбираете все записи из таблицы до достижения уровня, не имея других условий для ограничения результата. Для уровня <= 3 это делается снова для каждого из этих результатов.

Итак, для 3 записей:

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

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

Ответ 2

Если 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

Ответ 3

вы сравниваете яблоки с апельсинами при сравнении окончательного запроса с остальными, поскольку уровень LEVEL изолирован в 1-строчной двойной таблице.

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

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

что это такое, начните с набора таблицы (выберите * From a). то для каждой строки, возвращаемой, соединяет эту строку с предыдущей строкой. поскольку вы не определили соединение в соединении, это фактически является декартовым объединением, поэтому, когда у вас есть 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 плюс декартово множество).

поэтому количество строк output = rowcount + (rowcount ^ 2)

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

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

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

Ответ 4

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

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