質問

" foo"という2つのフィールドを持つデータベーステーブルがあるとします。および「バー」。どちらも一意ではありませんが、それぞれにインデックスが付けられています。ただし、一緒にインデックスが作成されるのではなく、それぞれに個別のインデックスがあります。

今、 SELECT * FROM sometable WHERE foo = 'hello' AND bar = 'world';などのクエリを実行すると仮定します。 fooが 'hello'である膨大な数の行とバーが「ワールド」である少数の行。

データベースサーバーが内部で行う最も効率的な方法は、バーインデックスを使用して、バーが「world」であるすべてのフィールドを検索し、fooが「hello」である行のみを返すことです。これは O(n)です。ここで、nはbarが「world」である行数です。

ただし、foインデックスが使用され、結果が検索されるプロセスは逆に発生する可能性があると思います。これは、 O(m)になります。mは、fooが「hello」である行数です。

では、Oracleはここで効率的に検索するのに十分スマートですか?他のデータベースはどうですか?または、クエリで適切な順序で検索するように指示する方法はありますか?おそらく、 WHERE 句で bar = 'world' を最初に置くことによって、

役に立ちましたか?

解決

Oracleはほぼ確実に最も選択的なインデックスを使用してクエリを実行しますが、Explain Planで確認できます。

さらに、Oracleは両方のインデックスの使用をいくつかの方法で組み合わせることができます-btreeインデックスをビットマップに変換してビットマップANd操作を実行するか、2つによって返されたROWIDでハッシュ結合を実行できますインデックス。

ここで重要な考慮事項の1つは、クエリ対象の値間の相関関係です。 foo = 'hello'がテーブルの値の80%を占め、bar = 'world'が10%を占める場合、Oracleはクエリがテーブル行の0.8 * 0.1 = 8%を返すと推定します。ただし、これは正しくない場合があります-クエリは、値の相関関係に応じて、実際にrwosの10%または行の0%を返す場合があります。現在、テーブル全体のこれらの行の分布によっては、インデックスを使用してそれらを見つけることは効率的ではない場合があります。 70%またはテーブルブロックにアクセスして必要な行(「クラスタリングファクター」のgoogle)を取得する必要がある場合があります。その場合、Oracleは推定が正しい場合、フルテーブルスキャンを実行します。

11gでは、このような状況に役立つ複数列の統計を収集できます。 9iおよび10gでは、動的サンプリングを使用して、取得する行数の非常に優れた推定値を取得できます。

実行計画を取得するには、次を実行します。

explain plan for
SELECT *
FROM   sometable
WHERE  foo='hello' AND bar='world'
/
select * from table(dbms_xplan.display)
/

以下と対比:

explain plan for
SELECT /*+ dynamic_sampling(4) */
       *
FROM   sometable
WHERE  foo='hello' AND bar='world'
/
select * from table(dbms_xplan.display)
/

他のヒント

はい、「ヒント」を与えることができます; Oracleへのクエリで。これらのヒントは、データベースへのコメント(" / * HINT * /&quot)として偽装されており、主にベンダー固有のものです。したがって、1つのデータベースの1つのヒントは、他のデータベースでは機能しません。

ここでは、小さなテーブルの最初のヒントであるインデックスヒントを使用します。 こちらを参照してください。

一方、これらの2つのフィールドを頻繁に検索する場合、これら2つのフィールドにインデックスを作成してみませんか?正しい構文はありませんが、次のようなものになります

CREATE INDEX IX_BAR_AND_FOO on sometable(bar,foo);

これにより、データの取得は非常に高速になります。また、連結が一意である場合は、高速な一意のインデックスを作成するだけです。

エリ、

あなたが書いたコメント:

  

残念なことに、それぞれに独自のインデックスを持つ多数の列を持つテーブルがあります。ユーザーはフィールドの任意の組み合わせを照会できるため、各フィールドの組み合わせでインデックスを効率的に作成することはできません。ただし、インデックスが必要なフィールドが2つしかない場合は、2つのインデックスを使用するという提案に完全に同意します。 –エリ・コートライト(9月29日15:51)

これは実際にはかなり重要な情報です。質問をするとき、プログラマーは自分を裏切ることがあります。彼らは質問を重要なポイントにまで絞り込もうとしますが、非常に頻繁に単純化しすぎて、最良の答えが得られません。

このシナリオは、ビットマップインデックスが発明された理由です-不明な列のグループがwhere句で使用される時間を処理するために。

BMIはカーディナリティの低い列専用であり、あなたのケースには当てはまらないかもしれないと誰かが言った場合に備えて。低はおそらくあなたが思うほど小さくはありません。唯一の実際の問題は、テーブルに対するDMLの同時実行です。これが機能するには、シングルスレッドまたはまれである必要があります。

  

つまり、Oracleは検索に十分なほどスマートです   ここで効率的に?

単純な答えは「おそらく」です。クエリオプティマイザーの最適化に取り組んでいる各データベースベンダーには、非常に優秀な人が大勢います。そして、統計を更新すると、おそらくさらに多くのことが行われます。

最初に、あなたが素敵で、通常の、標準的なb * -treeインデックスについて話していると仮定します。ビットマップインデックスの答えは根本的に異なります。また、Oracleにはさまざまなタイプのインデックスに対応する多くのオプションがあり、回答を変更する場合と変更しない場合があります。

少なくとも、オプティマイザーが特定の条件の選択性を決定できる場合、より選択性の高いインデックス(つまり、バー上のインデックス)を使用します。ただし、データが歪んでいる場合(列バーにN値がありますが、特定の値の選択度がデータの1 / Nよりも大幅に大きいか小さい場合)、列にヒストグラムを表示する必要がありますどの値が多かれ少なかれオプティマイザか。また、Oracleのバージョンによっては、バインド変数を使用している場合(すべての優れたOLTP開発者がそうするように)、バインド変数のピークに問題がある可能性があります。

潜在的に、Oracleは2つのb * -treeインデックスをビットマップにオンザフライで変換し、ビットマップを結合して、両方のインデックスを使用して取得する必要のある行を見つけることもできます。しかし、これはかなり珍しいクエリプランです。特に、1つの列が高度に選択されている2つの列しかない場合です。

どのインデックスが最初に使用されているかを正確に確認できるように、Oracleにクエリプランを表示させることもできます。

使用するインデックスに関するヒントを提供できます。私はOracleに慣れていませんが、MysqlではUSE | IGNORE | FORCE_INDEXを使用できます(こちらをご覧ください)。最高のパフォーマンスを得るには、結合インデックスを使用する必要があります。

最善のアプローチは、fooをbarのインデックスに追加するか、barをfooのインデックスに追加する(またはその両方)ことです。 fooのインデックスにbar上のインデックスも含まれている場合、その追加のインデックスレベルは、そのインデックスの現在の使用におけるfooインデックスの有用性に影響を与えず、そのインデックスを維持するパフォーマンスにも明らかな影響を与えませんが、データベースに追加を与えます例などのクエリの最適化で使用する情報。

それよりも優れています。

インデックスシークは、テーブル全体のスキャンよりも常に高速です。そのため、舞台裏でOracle(およびその点でSQLサーバー)は、最初に両方のインデックスの行の範囲を特定します。次に、どちらの範囲が短いか(内部結合であることがわかります)を調べ、短い範囲を反復して、2つの大きい方と一致するものを見つけます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top