4

no_indexが実際にクエリを高速化する方法を理解しようとしていますが、それを説明するドキュメントをオンラインで見つけることができませんでした。

たとえば、実行速度が非常に遅いこのクエリがあります

select  * 
    from    <tablename>
    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString' and
            Action_='_someAction_' and 
            Timestamp_ >= trunc(sysdate - 2)

そして、私たちのDBAの1人は、これを行うことで大幅にスピードアップすることができました

select  /*+ NO_INDEX(TAB_000000000019) */ * 
    from    <tablename>
    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString' and
            Action_='_someAction_' and 
            Timestamp_ >= trunc(sysdate - 2) 

そして、私は理由がわかりませんか?実行にさらに時間がかかるため、これが機能する理由を理解して、別のクエリ(これは結合)に適用して速度を上げることができるかどうかを確認したいと思います。

ありがとう!


**更新**例のテーブルについて私が知っていることは次のとおりです。

  • これは「パーティションテーブル」です
  • TAB_000000000019はテーブルであり、その中の列ではありません
  • field1にインデックスが付けられます
4

6 に答える 6

9

Oracleのオプティマイザは、クエリを実行するのに最適な方法を判断します。これを行うには、テーブルとインデックスに関して収集された多数の統計を使用します。これらの統計に基づいて、たとえば、インデックスを使用するかどうか、または単にテーブルスキャンを実行するかどうかを決定します。

重要なことに、これらの統計は収集に非常に費用がかかる可能性があるため、自動的に最新ではありません。統計が最新でない場合、オプティマイザーは「間違った」決定を下す可能性があり、テーブルスキャンを実行する方が実際に高速である場合はインデックスを使用する可能性があります。

これがDBA/開発者に知られている場合NO_INDEX、オプティマイザーにヒント(これが何であるか)を与えて、特定のインデックスを使用しないように指示することができます。

あなたの例でTAB_000000000019は、はインデックスまたはテーブルを参照します(自動生成された名前のように見えるので、インデックスを推測しています)。

正直なところ、少しブラックアートですが、私が理解しているように、それがその要点です。

免責事項:私はDBAではありませんが、その分野に手を出しました。

于 2011-01-20T17:49:19.447 に答える
3

更新ごと:field1が唯一のインデックス付きフィールドである場合、元のクエリはそのインデックスで高速フルスキャンを実行していた可能性があります(つまり、インデックス内のすべてのエントリを読み取り、field1のフィルター条件をチェックします)。テーブル内の行を見つけて、他の条件でフィルタリングします。field1の条件は、インデックスの一意のスキャンまたは範囲スキャン(つまり、インデックス内の特定の値または値の範囲の検索)が不可能になるようなものです。

field1に2つのフィルター述部があるため、オプティマイザーがこのパスを選択した可能性があります。オプティマイザーは、これらのそれぞれの推定選択性を計算し、次にそれらを乗算して、それらを組み合わせた選択性を決定します。ただし、多くの場合、これは条件に一致する行数を大幅に過小評価します。

NO_INDEXヒントは、オプティマイザーの考慮からこのオプションを除外するため、基本的に次善の策と考えられます。この場合、クエリ内の他のフィルター条件の1つに基づくパーティション削除を使用します。

于 2011-01-21T13:59:56.340 に答える
2

インデックスを使用すると、インデックスを使用してテーブルをクエリする場合と比較して、ディスクIOが増える場合、クエリのパフォーマンスが低下します。

これは、簡単な表で示すことができます。

create table tq84_ix_test (
  a number(15) primary key,
  b varchar2(20),
  c number(1)
);

次のブロックは、このテーブルに100万レコードを入力します。250番目ごとのレコードはrare valueb列にaで埋められ、他のすべてのレコードは:で埋められfrequent valueます。

declare
  rows_inserted number := 0;
begin

  while rows_inserted < 1000000  loop

        if mod(rows_inserted, 250) = 0 then

           insert into tq84_ix_test values (
               -1 * rows_inserted, 
               'rare value',
                1);

            rows_inserted := rows_inserted + 1;

        else

           begin
              insert into tq84_ix_test values (
                 trunc(dbms_random.value(1, 1e15)),
                'frequent value',
                 trunc(dbms_random.value(0,2))
               );
               rows_inserted := rows_inserted + 1;

           exception when dup_val_on_index then 
               null;
           end;

        end if;

  end   loop;

end;
/

列にインデックスが付けられます

create index tq84_index on tq84_ix_test (b);

同じクエリですが、1回はインデックスあり、もう1回はインデックスなしで、パフォーマンスが異なります。自分でチェックしてください:

set timing on


select /*+ no_index(tq84_ix_test) */
    sum(c)
  from 
    tq84_ix_test
  where
    b = 'frequent value';


select /*+ index(tq84_ix_test tq84_index) */
    sum(c)    
  from 
    tq84_ix_test
  where
    b = 'frequent value';

それはなぜです?インデックスがない場合は、すべてのデータベースブロックが順番に読み取られます。通常、これはコストがかかるため、悪いと見なされます。通常の状況では、インデックスを使用すると、このような「全表スキャン」は、たとえば2〜5個のインデックスデータベースブロックの読み取りに加えて、インデックスが指すレコードを含む1つのデータベースブロックの読み取りに減らすことができます。ここでの例では、まったく異なります。インデックス全体が読み取られ、インデックス内の(ほぼ)各エントリについて、データベースブロックも読み取られます。したがって、テーブル全体が読み取られるだけでなく、インデックスも読み取られます。この動作はc、インデックスにも含まれている場合は異なることに注意してください。その場合、Oracleはcテーブルに迂回する代わりに、インデックスからの値を取得することを選択できます。

したがって、問題を一般化するために、インデックスが少数のレコードを選択しない場合は、それを使用しないことが有益である可能性があります。

于 2011-01-20T20:36:52.553 に答える
2

インデックスについて注意すべき点は、インデックスは行の順序とフィールドのデータに基づいて事前に計算された値であるということです。この特定のケースでは、field1にインデックスが付けられており、次のようにクエリで使用していると言います。

    where   field1_ like '%someGenericString%' and 
            field1_ <> 'someSpecificString'

上記のクエリスニペットでは、パーセント(%)文字が文字列をクレードルし、次に別の特定の文字列をクレードルするため、フィルターは両方の可変データにあります。これは、オプティマイザヒントを使用しないデフォルトのOracle最適化では、最初にインデックス付きフィールド内の文字列を検索し、そのデータがフィールド内のデータのサブ文字列であるかどうかを確認してから、次のことを確認することを意味します。データが別の特定の文字列と一致しません。インデックスがチェックされた後、他の列がチェックされます。これを繰り返すと、非常に遅いプロセスになります。

DBAによって提案されたNO_INDEXヒントは、インデックスを使用するオプティマイザーの設定を削除し、オプティマイザーが最初に高速な比較を選択できるようにし、必ずしも最初にインデックス比較を強制してから他の列を比較できるようにします。

以下は、文字列とそのサブ文字列を比較するため、低速です。

            field1_ like '%someGenericString%'

以下は特定のものであるため、より高速です。

            field1_ like 'someSpecificString'

したがって、NO_INDEXヒントを使用する理由は、インデックスの比較で処理速度が低下する場合です。インデックスフィールドをより具体的なデータと比較する場合、通常、インデックスの比較はより高速です。

私が通常言うのは、上記の@Atishの例のように、インデックス付きフィールドに冗長なデータが含まれている場合、正の比較が返される前に、比較の負の長いリストを調べる必要があるためです。データベース設計とテーブル内のデータの両方がクエリの実行速度に影響を与えるため、ヒントはさまざまな結果を生成します。したがって、ヒントを適用するには、オプティマイザーにヒントを与える個々の比較がデータセットで高速になるかどうかを知る必要があります。このプロセスにはショートカットはありません。ヒントは実際のデータに基づいている必要があるため、適切なSQLクエリが記述された後にヒントを適用する必要があります。

このヒントリファレンスを確認してください:http://docs.oracle.com/cd/B19306_01/server.102/b14211/hintsref.htm

于 2012-06-29T10:02:46.270 に答える
0

'%someGenericString%'のようなクエリの前に%を使用すると、OracleがそのフィールドのINDEXを無視することにつながることをどこかで読んだことがあります。たぶんそれがクエリの実行が遅い理由を説明しています。

于 2013-02-06T11:18:32.737 に答える
0

Rene'とDaveが言ったことに加えて、これは私が実際に制作状況で観察したことです。

インデックス付きフィールドの条件があまりにも多くの一致を返す場合、Oracleは全表スキャンを実行することをお勧めします。

非常に大きなインデックス付きテーブルをクエリするレポートプログラムがありました。インデックスはリージョンコード上にあり、クエリは正確なリージョンコードを指定したため、OracleCBOはインデックスを使用します。

残念ながら、1つの特定の地域コードがテーブルエントリの90%を占めていました。

レポートが他の(マイナー)リージョンコードのいずれかで実行されている限り、30分未満で完了しましたが、メジャーリージョンコードでは何時間もかかりました。

SQLにヒントを追加して、全表スキャンを強制すると、問題が解決しました。

お役に立てれば。

于 2011-01-21T23:17:24.467 に答える