12

サイクルが最上位ノード (ルート ノードがルート ノードに直接接続されている) で発生したときに、Oracle が循環ループを超えてパスをたどり続ける理由を知っている人はいますか? さらに重要なことは、それをどのように防ぐか?

私は Oracle 11g Release 2 (11.2) を使用しており、階層クエリを調査しています。Oracle Database SQL言語リファレンスのページ9-4の図9-1のツリー構造を中心に質問を作成します。

ベンダーと顧客の概念を使用して、このツリーのテーブル構造を作成しました。

    create table t
     ( vendor       varchar2(3)
    , customer   varchar2(3)
    );
    insert into t values ( '1'  , '2'  ); 
    insert into t values ( '2'  , '3'  ); 
    insert into t values ( '2'  , '4'  ); 
    insert into t values ( '4'  , '5'  ); 
    insert into t values ( '4'  , '6'  ); 
    insert into t values ( '1'  , '7'  ); 
    insert into t values ( '7'  , '8'  ); 
    insert into t values ( '1'  , '9'  ); 
    insert into t values ( '9'  , '10' ); 
    insert into t values ( '10' , '11' ); 
    insert into t values ( '9'  , '12' ); 
    commit;

次の選択クエリは、問題なくツリーをトラバースします。

    select vendor, 
           customer, 
           level, 
           connect_by_isleaf as isleaf, 
           connect_by_iscycle as iscycle, 
           connect_by_root vendor||sys_connect_by_path(customer,' ~ ') as path 
    from t
    connect by nocycle
          vendor=prior customer
    start with vendor='1';

結果を与える:

Vendor Cust     Level   Isleaf Iscycle   Path
1        2        1        0        0   1 ~ 2
2        3        2        1        0   1 ~ 2 ~ 3
2        4        2        0        0   1 ~ 2 ~ 4
4        5        3        1        0   1 ~ 2 ~ 4 ~ 5
4        6        3        1        0   1 ~ 2 ~ 4 ~ 6
1        7        1        0        0   1 ~ 7
7        8        2        1        0   1 ~ 7 ~ 8
1        9        1        0        0   1 ~ 9
9        10       2        0        0   1 ~ 9 ~ 10
10       11       3        1        0   1 ~ 9 ~ 10 ~ 11
9        12       2        1        0   1 ~ 9 ~ 12

次に、構造にサイクルを追加して複雑にしました。まず、自分自身に販売するベンダーの記録…</p>

    --self cycle
    insert into t values ( '4'  , '4'  ); 

もう 1 つは、顧客がベンダーのベンダーであるベンダーの場合です…</p>

    --ancestor cycle
    insert into t values ( '6'  , '2'  ); 

上記の選択クエリを再実行すると、3 行目と 5 行目 (パス 1 ~ 2 ~ 4 および 1 ~ 2 ~ 4 ~ 6) の Iscycle が 1 であることを除いて、上記と同じ出力が得られます。CONNECT BY 命名法は、実際にサイクルを完了する子レコードではなく、サイクルの親レコードにフラグを立てることに注意してください。(したがって、4 と 6 の両方が祖先に戻ることは知っていますが、どの祖先かはわかりません。)

さらに 2 つのレコードを追加すると、元のツリーのブランチ全体でより大きなサイクルが作成されます。

 --cycle crossing branches of tree
  insert into t values ( '6'  , '9'  ); 
  insert into t values ( '11' , '2'  );  

選択クエリを再度実行すると、次の出力が得られます。

Vendor Customer Level   Isleaf   Iscycle       Path
1        2        1        0        0    1 ~ 2
2        3        2        1        0    1 ~ 2 ~ 3
2        4        2        0        1    1 ~ 2 ~ 4
4        5        3        1        0    1 ~ 2 ~ 4 ~ 5
4        6        3        0        1    1 ~ 2 ~ 4 ~ 6
6        9        4        0        0    1 ~ 2 ~ 4 ~ 6 ~ 9
9       10        5        0        0    1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10
10      11        6        1        1    1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10 ~ 11
9       12        5        1        0    1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 12
1        7        1        0        0    1 ~ 7
7        8        2        1        0    1 ~ 7 ~ 8
1        9        1        0        0    1 ~ 9
9       10        2        0        0    1 ~ 9 ~ 10
10      11        3        0        0    1 ~ 9 ~ 10 ~ 11
11       2        4        0        0    1 ~ 9 ~ 10 ~ 11 ~ 2
2        3        5        1        0    1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 3
2        4        5        0        1    1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4
4        5        6        1        0    1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 5
4        6        6        1        1    1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 6
9       12        2        1        0    1 ~ 9 ~ 12

出力は引き続き期待どおりです。すべてのサイクルにフラグが立てられ、サイクルが発生するとマッピングが停止します。

さて、問題の子… ルート ノードにセルフ サイクルを追加しましょう。これは、ノード 4 で作成した最初のサイクルとまったく同じです。ノード1のみ。

    insert into t values ( '1'  , '1'  );

今度は Oracle がノード 1 でサイクルを検出します。ただし、このサイクルを過ぎても継続し、ツリー構造全体を 2 回構築します。行 2 から 21 は、行 22 から 41 の複製であり、パスの先頭にノード 1 のサイクルが追加されています。

Vendor Customer  Level Isleaf Iscycle    Path
1        1        1        0    1      1 ~ 1
1        2        2        0    0      1 ~ 1 ~ 2
2        3        3        1    0      1 ~ 1 ~ 2 ~ 3
2        4        3        0    1      1 ~ 1 ~ 2 ~ 4
4        5        4        1    0      1 ~ 1 ~ 2 ~ 4 ~ 5
4        6        4        0    1      1 ~ 1 ~ 2 ~ 4 ~ 6
6        9        5        0    0      1 ~ 1 ~ 2 ~ 4 ~ 6 ~ 9
9       10        6        0    0      1 ~ 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10
10      11        7        1    1      1 ~ 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10 ~ 11
9       12        6        1    0      1 ~ 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 12
1        7        2        0    0      1 ~ 1 ~ 7
7        8        3        1    0      1 ~ 1 ~ 7 ~ 8
1        9        2        0    0      1 ~ 1 ~ 9
9       10        3        0    0      1 ~ 1 ~ 9 ~ 10
10      11        4        0    0      1 ~ 1 ~ 9 ~ 10 ~ 11
11       2        5        0    0      1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2
2        3        6        1    0      1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 3
2        4        6        0    1      1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4
4        5        7        1    0      1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 5
4        6        7        1    1      1 ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 6
9       12        3        1    0      1 ~ 1 ~ 9 ~ 12
1        2        1        0    0      1 ~ 2
2        3        2        1    0      1 ~ 2 ~ 3
2        4        2        0    1      1 ~ 2 ~ 4
4        5        3        1    0      1 ~ 2 ~ 4 ~ 5
4        6        3        0    1      1 ~ 2 ~ 4 ~ 6
6        9        4        0    0      1 ~ 2 ~ 4 ~ 6 ~ 9
9       10        5        0    0      1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10
10      11        6        1    1      1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10 ~ 11
9       12        5        1    0      1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 12
1        7        1        0    0      1 ~ 7
7        8        2        1    0      1 ~ 7 ~ 8
1        9        1        0    0      1 ~ 9
9       10        2        0    0      1 ~ 9 ~ 10
10      11        3        0    0      1 ~ 9 ~ 10 ~ 11
11       2        4        0    0      1 ~ 9 ~ 10 ~ 11 ~ 2
2        3        5        1    0      1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 3
2        4        5        0    1      1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4
4        5        6        1    0      1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 5
4        6        6        1    1      1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 6
9       12        2        1    0      1 ~ 9 ~ 12

1-1 サイクルが 4-4 サイクルと同じに扱われないのはなぜですか? 私は何が欠けていますか?

これを軽減するために、CONNECT BY 句に、顧客が「1」でないことを要求する条件を追加しました。

    select vendor, 
           customer, 
           level, 
           connect_by_isleaf as isleaf, 
           connect_by_iscycle as iscycle, 
           connect_by_root vendor||sys_connect_by_path(customer,' ~ ') as path 
    from t
    connect by nocycle
          vendor=prior customer
          and customer<>'1' 
    start with vendor='1';

皮肉なことに、これが行ったのは、行 1 からサイクル フラグを削除したことだけです。

どんな助けでも大歓迎です。

4

4 に答える 4

1

Oracleroot row(s)は、階層 (START WITH 条件を満たす行) を選択します。Oracle は、各ルート行の子行を選択します。各子行CONNECT BYは、ルート行の 1 つに関する条件の条件を満たす必要があります。

親行の子を検索するために、Oracle は親行の CONNECT BY 条件の PRIOR 式を評価し、表の各行の他の式を評価します。条件が真の行は、親の子です。条件には、クエリによって選択された行をさらにフィルター処理するための他のCONNECT BY条件を含めることができます。

A root row is the highest row within an inverted tree. 

子と同じ親 (22 または 33 または 44) で試してみると、それらはルート行ではなく単なる親であるため機能します。

同様に、出力の重複が発生connect by works on root which is duplicatedします。

Oracle is not able to restrict the uniqueness since Oracle can't give preference to one of the other

データセットを一意にするか、オラクルが階層内で優先的に機能するようにコーディングします

フォローアップ: OP の問題の解決策

SELECT
      VENDOR,
      CUSTOMER,
      LEVEL,
      CONNECT_BY_ISLEAF AS ISLEAF,
      CONNECT_BY_ISCYCLE AS ISCYCLE,
      CONNECT_BY_ROOT VENDOR
      || SYS_CONNECT_BY_PATH ( CUSTOMER,
                          ' ~ ' )
          AS PATH
FROM
      (SELECT
            VENDOR,
            CUSTOMER
       FROM
            T
       WHERE
            CUSTOMER <> '1')
CONNECT BY
      NOCYCLE VENDOR = PRIOR CUSTOMER
START WITH
      VENDOR = '1';

結果:

VENDOR CUSTOMER      LEVEL     ISLEAF    ISCYCLE PATH                                                                            
------ -------- ---------- ---------- ------------------------------------------------------------------------------------------
1      2                 1          0          0 1 ~ 2                                                                           
2      3                 2          1          0 1 ~ 2 ~ 3                                                                       
2      4                 2          0          1 1 ~ 2 ~ 4                                                                       
4      5                 3          1          0 1 ~ 2 ~ 4 ~ 5                                                                   
4      6                 3          0          1 1 ~ 2 ~ 4 ~ 6                                                                   
6      9                 4          0          0 1 ~ 2 ~ 4 ~ 6 ~ 9                                                               
9      10                5          0          0 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10                                                          
10     11                6          1          1 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10 ~ 11                                                     
9      12                5          1          0 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 12                                                          
1      7                 1          0          0 1 ~ 7                                                                           
7      8                 2          1          0 1 ~ 7 ~ 8                                                                       
1      9                 1          0          0 1 ~ 9                                                                           
9      10                2          0          0 1 ~ 9 ~ 10                                                                      
10     11                3          0          0 1 ~ 9 ~ 10 ~ 11                                                                 
11     2                 4          0          0 1 ~ 9 ~ 10 ~ 11 ~ 2                                                             
2      3                 5          1          0 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 3                                                         
2      4                 5          0          1 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4                                                         
4      5                 6          1          0 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 5                                                     
4      6                 6          1          1 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 6                                                     
9      12                2          1          0 1 ~ 9 ~ 12                                                                      

20 rows selected
于 2013-09-23T13:29:36.847 に答える
1

Oracleが階層データをどのように扱うかについての@realspiritualsの説明の最初の部分に同意します。私のビジョンでは、最初のステップは、START WITH 句で指定されたツリーのルート要素を見つけることです。これは、次のクエリに言い換えることができます。

select * from t where vendor = '1';
VENDOR  CUSTOMER
------------------
1   2
1   7
1   9
1   1

したがって、実際には 4 つのルート ノードと 4 つの個別のツリーがあります。次のステップは、CONNECT BY 句を繰り返し評価することです。上記の CUSTOMER 値のリストを取得し、その子孫を探していると想像してください。

select * from t where vendor in ('2', '7', '9', '1');
VENDOR  CUSTOMER
------------------
1   2
2   3
2   4
1   7
7   8
1   9
9   10
9   12
1   1 --This one is loop and is not taken to final resultset

NOCYCLE を指定するとすぐに、検出されたループが破棄され、ループ レコードにつながった前の行が CONNECT_BY_ISCYCLE = 1 としてマークされます。

3 番目のステップ:

select * from t where vendor in ('2', '3', '4', '7', '8', '9', '10', '12');
VENDOR  CUSTOMER
------------------
2   3
2   4
4   5
4   6
7   8
9   10
10  11
9   12
4   4 --This one is loop

したがって、少なくとも 1 つのレコードが出力されるまで続きます。ある程度の時間と忍耐が必要ですが、クエリによって返される結果は完全に再現可能であり、私には完全に正当に思えます。これがオラクルのアルゴリズムの仕組みなので、クエリを作成するときは誰もがそれを念頭に置いておく必要があります。

最上位ノードでサイクルを回避するにはどうすればよいですか? 最上位ノードを最上位ノードにしない仮想レコードを追加することをお勧めします。このことを考慮:

insert into t values(null, '1');

select vendor, 
       customer, 
       level, 
       connect_by_isleaf as isleaf, 
       connect_by_iscycle as iscycle, 
       connect_by_root vendor||sys_connect_by_path(customer,' ~ ') as path 
from t
connect by nocycle
      vendor=prior customer
start with vendor is null; --Note the changed condition

Vendor Customer Level   Isleaf  Iscycle  Path
------------------------------------------------------------
        1       1       0       1        ~ 1
1       2       2       0       0        ~ 1 ~ 2
2       3       3       1       0        ~ 1 ~ 2 ~ 3
2       4       3       0       1        ~ 1 ~ 2 ~ 4
4       5       4       1       0        ~ 1 ~ 2 ~ 4 ~ 5
4       6       4       0       1        ~ 1 ~ 2 ~ 4 ~ 6
6       9       5       0       0        ~ 1 ~ 2 ~ 4 ~ 6 ~ 9
9       10      6       0       0        ~ 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10
10      11      7       1       1        ~ 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 10 ~ 11
9       12      6       1       0        ~ 1 ~ 2 ~ 4 ~ 6 ~ 9 ~ 12
1       7       2       0       0        ~ 1 ~ 7
7       8       3       1       0        ~ 1 ~ 7 ~ 8
1       9       2       0       0        ~ 1 ~ 9
9       10      3       0       0        ~ 1 ~ 9 ~ 10
10      11      4       0       0        ~ 1 ~ 9 ~ 10 ~ 11
11      2       5       0       0        ~ 1 ~ 9 ~ 10 ~ 11 ~ 2
2       3       6       1       0        ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 3
2       4       6       0       1        ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4
4       5       7       1       0        ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 5
4       6       7       1       1        ~ 1 ~ 9 ~ 10 ~ 11 ~ 2 ~ 4 ~ 6
9       12      3       1       0        ~ 1 ~ 9 ~ 12

もちろん、本番データベースに新しいレコードを追加するのは適切ではないかもしれません。むしろ、最上位ノードを動的に決定するいくつかのクエリを使用して、実際のテーブルへのクエリを組み合わせます。そのようなもの(上記と同じ出力を与える):

delete from t where vendor is null; --Removing previosly inserted record

select vendor, 
       customer, 
       level, 
       connect_by_isleaf as isleaf, 
       connect_by_iscycle as iscycle, 
       connect_by_root vendor||sys_connect_by_path(customer,' ~ ') as path 
from (select vendor, customer from t
      union all
      select distinct null, vendor from t
      where vendor = 1) --Here is your START WITH condition
connect by nocycle
      vendor=prior customer
start with vendor is null;
于 2013-12-03T22:02:23.787 に答える
-1

実際には、クエリにサイクルを含めることができます。nocycleそのキーワードがないと、Oracle はサイクルが検出されるとすぐに停止します ( ORA-01436: CONNECT BY loop in user data)。また、「CONNECT_BY_ISCYCLE」を使用して子供が循環している場所を検出することもできますが、その結果でクエリをフィルター処理すると、有効な行が削除されます。それで、おそらく、connect by nocycle vendor=prior customer AND connect_by_iscycle = 0ループの親が検出された後、すべてのループを回避するためにループ条件を使用できますか? (私はそれをテストするものを持っていません)。これにより、最初の 1~1 パスで再帰性が停止します。

于 2013-09-30T09:15:12.337 に答える
-1

ノードから開始して、あるノードを別のノードに接続することは同じではありません。ISCYCLE顧客からベンダーへの接続を探し、パスごとに 1 回だけ接続します。オラクルに言ったら

START WITH vendor = '1'

実際には 4 つのポイントで同時に開始します。

1 ~ 1
1 ~ 2
1 ~ 7
1 ~ 9

これらのパス検索は並行して実行され、すべてのパスは独自のパスで循環しないようにします。各パスは、他のパスについて何も知りません。したがって、 で始まるパスは、 2、7、9 への1 ~ 1継続を停止する必要がある理由がわかりません。これまでに存在しなかったためです。NOCYCLEもう一度 1 を調べることは禁じられています。したがって、次のいずれかを行うことができます

START WITH (vendor='1' AND customer !='1')

あまりにも多くの出発点を避けたり、ベンダーと顧客が同じである接続を無視したりするには、次のようにします。

CONNECT BY NOCYCLE ( vendor = PRIOR customer AND vendor != customer )
于 2013-09-30T00:07:37.527 に答える