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
列LVLの値が1である表Aの各行と、列LVLが2である表Aの各行に3行
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行を返す理由、またはID列の各値に対してLVLが2で3行があり、LVLが1であるのは1行だけである理由がわかりません。
「接続」されているレベルの数を3に増やす 13行を返す IDの各値に対して。 LVLが1の場合1、LVLが2の場合3、LVLが3の場合9.
ただし、これらのクエリは次のようになり、6行が返されます。
select id, lvl
from ( select level as lvl
from dual
connect by level <= 2
)
cross join a
order by id, lvl
documentation は、何が起こるべきかを説明する上で、私には特に明確ではありません。これらの権限で何が起きているのですか?最初の2つのクエリが3番目のクエリと同じではないのはなぜですか?
最初のクエリでは、レベルだけで接続します。したがって、レベル<= 1の場合、各レコードを1回取得します。レベル<= 2の場合、各レベル1回(レベル1の場合)+ N回(Nはテーブル内のレコード数)を取得します。結果を制限する他の条件がなくても、レベルに達するまでテーブルからすべてのレコードを選択するだけなので、クロスジョインのようです。レベル<= 3の場合、これらの結果ごとにこれが再度行われます。
したがって、3つのレコードの場合:
実際にはクロスジョインではありません。クロス結合は、このクエリ結果にレベル2のレコードのみを返しますが、この接続では、レベル1のレコードとレベル2のレコードを取得するため、結果は単なる3ではなく3 + 3 * 3になります。 3 * 3レコード。
connect by
句とprior
演算子なしでstart with
を使用する場合、子行を親行に結合することに制限はありません。そして、この状況で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
lEVELが1行のデュアルテーブルに分離されているため、最終クエリを他のクエリと比較するときに、リンゴとオレンジを比較しています。
このクエリを検討してみましょう:
select id, level as lvl
from a
connect by level <= 2
order by id, level
つまり、テーブルセットから始めます(*から選択)。次に、返された各行に対して、この行を前の行に接続します。 connect byで結合を定義していないので、これは実質的にデカルト結合です。したがって、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行(元の「レベル1」行3行とデカルト集合)を取得する理由です。
出力行数= rowcount +(rowcount ^ 2)
最後のクエリでは、これにレベルを分離しています
select level as lvl
from dual
connect by level <= 2
もちろん2行を返します。次に、これを元の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