4

ツリーを表す履歴推移閉包表があります。

create table TRANSITIVE_CLOSURE
  (
    CHILD_NODE_ID number not null enable,
    ANCESTOR_NODE_ID number not null enable,
    DISTANCE number not null enable,
    FROM_DATE date not null enable,
    TO_DATE date not null enable,
    constraint TRANSITIVE_CLOSURE_PK unique (CHILD_NODE_ID, ANCESTOR_NODE_ID, DISTANCE, FROM_DATE, TO_DATE)
  );

サンプルデータは次のとおりです。

CHILD_NODE_ID | ANCESTOR_NODE_ID | DISTANCE 
--------------------------------------------
1             | 1                | 0
2             | 1                | 1
2             | 2                | 0
3             | 1                | 2
3             | 2                | 1
3             | 3                | 0

残念ながら、ルート ノードを見つけるための現在のクエリでは、完全なテーブル スキャンが発生します。

select *
from transitive_closure tc
where 
  distance = 0
  and not exists (
  select null
  from transitive_closure tci
  where tc.child_node_id = tci.child_node_id
    and tci.distance <> 0
);

表面的にはそれほど高価には見えませんが、100 万行に近づくにつれて、この特定のクエリは厄介になり始めています... 特に、レガシー サポートのために隣接ツリーを取得するビューの一部である場合はそうです。

推移閉包のルート ノードを見つけるより良い方法はありますか? 古いレガシ コードをすべて書き直したいのですが、できません...そのため、何らかの方法で隣接リストを作成する必要があります。ルート ノード以外のすべてを取得するのは簡単ですが、より良い方法はありますか? 私はこの問題について間違った方法で考えていますか?

80 万行のテーブルに対するクエリ プラン。

OPERATION                                  OBJECT_NAME        OPTIONS         COST 
SELECT STATEMENT                                                              2301 
  HASH JOIN                                                   RIGHT ANTI      2301 
    Access Predicates
      TC.CHILD_NODE_ID=TCI.CHILD_NODE_ID 
    TABLE ACCESS                           TRANSITIVE_CLOSURE FULL            961 
      Filter Predicates 
        TCI.DISTANCE = 1 
    TABLE ACCESS                           TRANSITIVE_CLOSURE FULL            962 
      Filter Predicates 
        DISTANCE=0
4

3 に答える 3

2

クエリの実行にかかる時間はどれくらいですか? (通常、コストをチューニングに使用することは望ましくありません。説明計画のコストが実際に何を意味するかを知っている人はほとんどいません。)

私の遅いデスクトップでは、クエリは 800K 行で 1.5 秒しかかかりませんでした。そして、データがメモリに入ってから 0.5 秒後。何かが著しく悪化していますか、それともこのクエリは非常に頻繁に実行されますか?

データがどのように見えるかはわかりませんが、このクエリには常にフル テーブル スキャンが最適であると思います。階層データが比較的浅いと仮定すると、つまり、0 と 1 の距離が多く、100 の距離がほとんどない場合、最も重要な列はそれほど明確ではありません。これは、距離のインデックス エントリのいずれかが多数のブロックを指すことを意味します。一度に 1 ブロックずつ大量のテーブルを読み取るよりも、マルチブロック読み取りを使用してテーブル全体を一度に読み取る方がはるかに安価です。

また、歴史的とはどういう意味ですか?このクエリの結果をマテリアライズド ビューに保存できますか?

別の考えられるアイデアは、分析関数を使用することです。これにより、2 番目のテーブル スキャンがソートに置き換えられます。通常、このアプローチの方が高速ですが、私の場合、このクエリは実際には 1.5 秒ではなく 5.5 秒かかります。しかし、おそらくあなたの環境ではうまくいくでしょう。

select * from
(
    select
        max(case when distance <> 0 then 1 else 0 end)
            over (partition by child_node_id) has_non_zero_distance
        ,transitive_closure.*
    from transitive_closure
)
where distance = 0
    and has_non_zero_distance = 0;
于 2011-03-12T06:13:27.587 に答える
0

現在のすべてのルート ノードの子孫である 1 つのルート ノードを追加します。次に、1 つのルートの子を照会するだけです。問題が解決しました。

于 2012-11-06T12:35:19.987 に答える
0

distance と child_node_id にインデックスを追加するか、既存の一意のインデックスでこれらの列の順序を変更できますか? 内側のクエリはインデックスへのアクセスのみを必要とするのに対し、外側のクエリは距離によってインデックスによってテーブルにアクセスできるはずだと思います。

于 2011-03-11T19:15:01.490 に答える