2011-08-02 3 views
8

他のインデックス付き列でフィルタリングされた列の上位200行を選択する簡単なクエリがあります。私はすべての行を選択していたときにPL/SQL Developerのクエリプランは、このインデックスが使用されていることを示していることを理由に混乱があるのみ、例えば:最上位の行を選択するときに間違ったインデックスが使用されています

SELECT * FROM 
(
SELECT * 
FROM cr_proposalsearch ps 
WHERE UPPER(ps.customerpostcode) like 'MK3%' 
ORDER BY ps.ProposalNumber DESC 
) 
WHERE ROWNUM <= 200 

計画はそれがインデックスCR_PROPOSALSEARCH_I1を、使用することを示しています2列のインデックス: query with ROWNUM

を私はROWNUM条件を取り除く場合は、計画は私が期待するものであり、それは0.343sで実行されます。PROPOSALNUMBER & UPPER(CUSTOMERNAME)、これは実行する0.985sを取ります: query without ROWNUM

index XIF25CR_PROPOSALSEARCH is on CR_PROPOSALSEARCH (UPPER(CUSTOMERPOSTCODE));

がどのように来ますか?

EDIT:私はcr_proposalsearch表の統計を収集しており、両方のクエリプランは、今、彼らはXIF25CR_PROPOSALSEARCHインデックスを使用することを示しています。

+0

2つの異なるクエリ、2つの異なる結果セット、2つの異なる説明計画が存在しない理由は何ですか? – APC

答えて

8

ROWNUMを含めると、より効率的なパスについてのオプティマイザの計算が変更されます。

このような上位n問合せを実行すると、必ずしもすべての行が取得され、完全にソートされてから上位のものが戻されるというわけではありません。実行計画の中のCOUNT STOPKEY操作では、要求された行数が検出されるまで、Oracleは基本操作のみを実行します。

オプティマイザは、完全な問合せが77K行を取得してソートすると計算しました。トップnのクエリでこの計画を使用した場合は、トップ200を見つけるために大量の行を実行する必要があります(必ずしも完全に並べ替える必要はありません。正確な順序は気にしませんその行のすべてを調べなければなりません)。

トップnクエリの計画では、他のインデックスを使用して並べ替えを行わないようにします。各行を順番に検討し、述部と一致するかどうかを調べ、一致する場合はそれを戻します。それが200行戻ったとき、それは完了です。その計算は、これが少数の行を得るためにより効率的であることを示しています。 (当然のことではないかもしれませんが、これらのクエリの相対的なパフォーマンスは何であるかは言いません。)

すべての行を尋ねるときにオプティマイザがこのプランを選択した場合は、インデックス全体を降順で読み込み、述部に対してチェックするときにROWIDによって各行をテーブルから取得する必要があります。これにより、多くの余分なI/Oが発生し、返されない行が数多く検査されます。この場合、customerpostcodeのインデックスを使用する方が効率的です。

トップnクエリから返される行の数を徐々に増やすと、プランが最初から2番目に切り替わるティッピングポイントが見つかる可能性があります。ちょうど2つのプランのコストから、私はこれが約1,200行であるかもしれないと思います。

+0

天才、閾値は1166です、どうやってこれを知ったのですか?私はタイミングと他のインデックスの詳細を追加しました。 @Kevin Burtonの答えに従ってインデックスを使用するようにクエリに指示すると、時間は常に約0.3秒です。 – Tsar

+0

plsに注意してください。私の最大個数は常に200になります。つまり、XIF25CR_PROPOSALSEARCHは決して使用されません。ここで使用できる別の最適化オプションがありますか? – Tsar

+0

私は、最初の計画のコストが線形に拡大すると考えました。 6 x 951 = 5,706、第2プランのコストは5,512に非常に近い。だから私は、転倒ポイントが6 x 200 = 1200に近いと推定した。 –

1

あなたは完全に適合するインデックスを持っていないようです。索引CR_PROPOSALSEARCH_I1を使用して、属性PROPOSALNUMBERの降順で行を取り出すことができます。これはおそらくOracleが一致するすべての行を検索し、ORDER BY句に従ってソートして、最初の行以外のすべての行を破棄することを避けることができるためです。

ROWNUM条件が指定されていないと、OracleはXIF25CR_PROPOSALSEARCH索引を使用します(詳細は示されませんでした)。しかし、後で結果をソートする必要があります。これはおそらく、すべての行を取得するという前提に基づいたより効率的な計画です。

どちらの索引もトレードオフであるため(ソートする方が優れており、WHERE句を適用する方が適しています)、ROWNUMなどの詳細によって、Oracleが選択する実行計画が決定されます。

4

あなたの統計情報が最新であるとインデックスが十分に選択的であることを、あなたは(私は最後の手段としてのみ、この方法をお勧めします)インデックス

SELECT * 
FROM (SELECT /*+ index(ps XIF25CR_PROPOSALSEARCH) */ * 
     FROM  cr_proposalsearch ps 
     WHERE UPPER (ps.customerpostcode) LIKE 'MK3%' 
     ORDER BY ps.proposalnumber DESC) 
WHERE ROWNUM <= 200 

を使用するようにOracleを言うことができる確信している場合

私は、私は、それはやっている、実際にどのくらいの仕事を参照するには、クエリTKPROF最初にこれをやっていただろう場合

例:索引レンジ・スキャンのコストは可能性が道オフ

SELECT count(*) FROM cr_proposalsearch ps WHERE UPPER(ps.customerpostcode) like 'MK3%' 

、その後、クエリプランで基数と比較:は、あなたが実際のカーディナリティをチェックする必要があります ....言及するのを忘れてしまいました。

1

この条件:

WHERE UPPER(ps.customerpostcode) like 'MK3%' 

あなたはそれのための単一の注文した範囲を維持することができないでいる、連続的ではありません。その後、コードに基づいてフィルタリング数によって

  1. 注文:

    だから、このクエリを実行するには、2つの方法があります。

  2. コードでフィルタリングしてから番号順に並べ替えます。

方法1は(トップ100行は、2倍の速トップ200より選択し、その番号を提供し、コードが相関していないことになる)ユーザーあなたに線形実行時間を与える数のインデックスすることができます。

メソッド2は、コードの粗いフィルタリング(範囲条件はcode >= 'MK3' AND code < 'MK4'のようになります)では範囲スキャンを使用できますが、複合インデックスでは数値の順序を保持できないため、並べ替えが必要です。

ソート時間は選択している最上行の数にもよりますが、この依存関係はメソッド1の場合と異なり、リニアではありません(少なくとも1つの範囲スキャンが常に必要です)。

ただし、方法2のフィルタ条件は、RANGE SCANでは十分に選択的であり、その後の並べ替えはテーブル全体のFULL SCANより効率的です。

これは転倒ポイントがあることを意味します。この条件の場合:の値が存在するため、この値を超えると効率的になる方法2です。

更新:あなたは常に、少なくとも3最初のシンボルを上の検索している場合は

、あなたがこのようなインデックスを作成することができます

SUBSTRING(UPPER(customerpostcode), 1, 3), proposalnumber 

をして、このクエリでそれを使用する:

SELECT * 
FROM (
     SELECT * 
     FROM cr_proposalsearch ps 
     WHERE SUBSTRING(UPPER(customerpostcode, 1, 3)) = SUBSTRING(UPPER(:searchquery), 1, 3) 
       AND UPPER(ps.customerpostcode) LIKE UPPER(:searchquery) || '%' 
     ORDER BY 
       proposalNumber DESC 
     ) 
WHERE rownum <= 200 

この方法では、最初にコードを共有するコードセットごとに番号の順序が保存されますより密なインデックススキャンを提供する文字。

関連する問題