2009-06-14 5 views
5

3つのテーブルを含む共通のデータベース結合状況があります。 1つのテーブルAは、idという名前の主キーを持つメインテーブルです。テーブルBおよびCは、エントリおよびAの補助データを含み、それぞれはidという名前の列を持ち、これはA. idを指す外部キーです。今、A、B、Cからのすべてのデータを1つのクエリで欲しいとすれば、私は次のように書いています。Oracleで還元剤結合条件を追加すると、異なる計画が発生する

SELECT * 
FROM A 
INNER JOIN B 
ON B.id = A.id 
INNER JOIN C 
ON C.id = A.id 

もちろん完璧に動作します。

最近、私たちのDBAは次のように、これはOracleのでは非効率的である、とあなたが同様にCとBの間の結合条件をする必要があることを私たちに語った:

SELECT * 
FROM A 
INNER JOIN B 
ON B.id = A.id 
INNER JOIN C 
ON C.id = A.id AND C.id = B.id 

これは私には冗長に見えたので、当然私はdidnのここで信じていない。実際には、実行計画がひどい低速なクエリに実際に遭遇し、不足している結合条件を正確に追加することによって問題を修正することができました。私は両方のバージョンで説明した計画を実行しました。「冗長」なクエリ条件のないものは1,035のコストでしたが、「改善された」ものは389でした​​(カーディナリティとバイトにも大きな違いがありました)。どちらのクエリでも同じ結果が得られました。

誰もこの余分な条件が違いをもたらす理由を説明できますか?私にはCとBは関連していません。また、他の結合条件を取り除くと、それは同じように悪いことに注意してください - 両方ともそこにいる必要があります。

答えて

2

2つの問題があります。

最初に、オプティマイザは、BのIDと一致する行を持つAの行の数について見積もります。これらの行もCに一致する行があります。推定値が不正確で、間違った計画が選択されています。

ここで、冗長条件を追加します。オラクルは、本当に冗長な条件はないと仮定しています(インテリジェントな開発者が含まれていない場合)。また、各条件は他の条件とは独立していると仮定します。たとえば、hair = 'bald'のselectはテーブルの10%を取得し、selectはgender = 'F'が50%になる場所を選択します。オラクルは、hair = 'bald'とgender = 'F'の5つの選択肢を選択すると想定しています(現実的には、脱毛症は主に男性に限定されています)。

「冗長」述語を追加することにより、オラクルは除外する数値または行を過大評価し、それに応じて計画を選択します。

冗長な述語を使用してOracleがより良い計画を選択している場合は、元の問合せの見積もりが一致する行の数を過大評価していることが示唆されます。冗長な述語は、過小評価でそれに対抗しています。この場合、2つの間違いが正しいとしています。

それは私がお勧めしたい解決策はないが、それが動作するかどうか.....

PS。私はすべてのIDのデータ型が一貫していると仮定しています。 B.IDとC.IDが日付で、A.IDが文字だった場合、またはその逆の場合は、A.ID = B.IDおよびA.ID = C.IDではB.IDですが、いくつかの行がある可能性があります。 = C.ID、暗黙的な変換はタイムスタンプを失う可能性があるためです。

+1

私はあなたに同意します。Gary:統計が不正確であるため、一般的に、冗長な情報は提供しないでください。 –

+0

これは私にとって最も説得力のある答えです。なぜなら、それはOracleに若干の希望を戻すからです。 (だから、私は少し不公平に偏っている。)実際の説明かどうかは、誰でも答えるのが難しい。 – waxwing

1

これらの2つのクエリは、私にとって全く同じではありません。
また、私はOracleオプティマイザではありません。あなたはこの

INNER JOIN B 
ON B.id = A.id 

結果のResultSetを行っているとき、BとCの両方がAの外部キーを持っているので

は、あなたが2番目のクエリでテーブルCに参加している上に小さい(数回の高速であると言います)実行計画の結果で見たように、テーブルを最初のクエリと同様にをテーブルAに結合する場合よりも、

したがって、AとBの交点であるより小さなデータセットのテーブルCを結合すると、はAとCの交点であるより小さなデータセットになります。

+1

心に残念です。 2番目の条件を追加すると、実際の結果セットのサイズに違いはありません。他の人が指摘したように、結果セット・サイズのオプティマイザの見積もり、つまり実行計画の変更が変更されます。最初のクエリでテーブルCを「テーブルAのみ」に参加させたと言うのは誤りです。彼はAとBに加わり、その結果セットにCに参加しました。結合条件はA内の列のみを参照するために発生しましたが、論理的に3つの表のID列は結合条件によって等しくなる必要があるため、使用される結果に関係なく結果は同じです。 –

+0

@Dave:その徹底した説明をありがとう。私はこの質問に貧しい答えを与えることから多くを学んだと思う。うまくいけば、誰かがこれからも学ぶことができます。 – bernie

2

オラクルのオプティマイザは、等価性に関する推移的な仮定をしません。 A = BおよびA = C、B = Cの場合、WHERE句またはJOIN条件に明示的に記述されていない限り、OracleはB & Cの間に関係があるとは考えていません。

テーブルの内容全体を選択するのではなく、A、B、および/またはCの他の制約があると仮定しています。そうしないと、テーブルが小さかった場合を除いてI /その時点で最適化はやや疑わしい)。ですから、あなたが指定するよりもA、B & Cにはもっと厳しい制約があります。 Oracleのオプティマイザは、FROM句内のすべての表を調べ、WHERE句に制約をリストし、それらの表の索引に基づいて制約の選択性を決定します。次に、攻撃計画のさまざまな順列を辿って、どれが最も欲しいかを決定します(これらの値は、計画で参照する基数値です)。 B = C条件がなければ、Bで始まりCに進む(またはその逆)計画は除外され、可能な限り最良の計画である可能性があります。

+0

はい、あなたの前提は正しいです。実際、私はこの質問を書いたときにオリジナルのクエリを見つけることさえできなかったので、私は上記の関係を持つ3つのテーブルを含む同様のものを書きました。私は、実際のクエリがさらに劇的な違いを生みだしていることを語っています。しかし、私は元のクエリでB = C条件(A = C条件ではない)のみを含めるように試みましたが、同様に悪い結果が得られました。私にとって奇妙なのは、A = CとB = Cの両方を持たなければならなかったということでした。 – waxwing

3

興味深い。

オラクル社では、この過渡的な等価性は、状況によっては、Transitive Closureと呼ばれ、問合せの再書込みが有効な場合に恩恵を受けることができます。

しかし、安全な側になるためには、冗長な述語を自分自身でよく書き直してください。

+0

興味深い記事。ありがとう! – waxwing

関連する問題