Oracle 10gがあり、1つのテーブル(結合なし)をクエリして、列の1つがnullである行を除外する必要があります。これを行うと-WHEREOurColumn IS NOTNULL-非常に大きなテーブルで全表スキャンを取得します-BADBAD BAD。列にはインデックスがありますが、このインスタンスでは無視されます。これに対する解決策はありますか?
ありがとう
オプティマイザは、全表スキャンの方が優れていると考えています。
NULL
行が数行しかない場合は、オプティマイザーが適切です。
インデックスへのアクセスが高速になることが確実な場合(つまり、75%
でcol1 IS NULL
を超える行がある場合)、クエリにヒントを与えます。
SELECT /*+ INDEX (t index_name_on_col1) */
*
FROM mytable t
WHERE col1 IS NOT NULL
なぜ75%
?
INDEX SCAN
を使用してインデックスでカバーされていない値を取得することは、ROWID
での非表示結合を意味するため、テーブルスキャンの約4
倍のコストがかかります。
インデックス範囲に25%
を超える行が含まれている場合、通常、テーブルスキャンの方が高速です。
Tony Andrews
で述べたように、クラスタリング係数はこの値を測定するためのより正確な方法ですが、25%
は依然として経験則です。
オプティマイザーは、全表スキャンの相対コストとインデックスの使用に基づいて決定を下します。これは主に、クエリを満たすために読み取る必要のあるブロックの数になります。別の回答で言及されている25%/ 75%の経験則は単純です。場合によっては、行の1%を取得する場合でも、全表スキャンが理にかなっています。つまり、これらの行がたまたま多くのブロックに分散している場合です。
たとえば、次の表について考えてみます。
SQL> create table t1 as select object_id, object_name from all_objects;
Table created.
SQL> alter table t1 modify object_id null;
Table altered.
SQL> update t1 set object_id = null
2 where mod(object_id,100) != 0
3 /
84558 rows updated.
SQL> analyze table t1 compute statistics;
Table analyzed.
SQL> select count(*) from t1 where object_id is not null;
COUNT(*)
----------
861
ご覧のとおり、T1の行の約1%のみがnull以外のobject_idを持っています。しかし、私がテーブルを作成した方法により、これらの861行はテーブル全体にほぼ均等に分散されます。したがって、クエリ:
select * from t1 where object_id is not null;
オプティマイザーがインデックスを使用した場合でも、T1のほぼすべてのブロックにアクセスしてデータを取得する可能性があります。次に、インデックスを省略して、全表スキャンを実行するのが理にかなっています。
この状況を特定するのに役立つ重要な統計は、インデックスのクラスタリング係数です。
SQL> select clustering_factor from user_indexes where index_name='T1_IDX';
CLUSTERING_FACTOR
-----------------
460
この値460は(インデックスの861行と比較して)非常に高く、全表スキャンが使用されることを示唆しています。 クラスタリング係数に関するこのDBAZineの記事 を参照してください。
Select *を実行している場合は、インデックスを使用するのではなく、テーブルスキャンを実行する方が理にかなっています。関心のある列がわかっている場合は、それらの列に加えて、IS NOTNULL条件を適用している列でカバーされたインデックスを作成できます。
これは、テーブルにあるインデックスのタイプによって異なります。
ほとんどのBツリーインデックスはnot nullエントリを格納します。ビットマップインデックスdo nullエントリを格納します。
だから、あなたが持っているなら:
mycolumnがnullであるmytableから*を選択します
mycolumn
に標準のBツリーインデックスがある場合、クエリできません「null」がインデックスにないため、インデックスを使用します。
(インデックスが複数の列に対してあり、インデックス付きの列の1つがnullでない場合、インデックスにエントリがあります。)
Oracleデータベースは通常の(bツリー)インデックスでnull値にインデックスを付けないため、null値を使用することも、Oracleデータベースに強制的に使用させることもできません。
BR
テーブル上のOracleの統計が最新であるかどうかを確認することも価値があります。全表スキャンが遅くなることを知らない場合があります。
ヒントの使用は、解決策ではなく回避策としてのみ実行する必要があります。
他の回答で述べたように、null値はB-TREEインデックスでは使用できません。
この列にはほとんどnull値があることがわかっているので、たとえばnull値を範囲に置き換えることができますか。
これは実際には列とデータの性質によって異なりますが、通常、列が日付タイプの場合は次のようになります。
where mydatecolumn is not null
次のようなルールで翻訳できます:日付のあるすべての行が必要です。
次に、これを最も確実に行うことができます。ここで、mydatecolumn <= sysdate(Oracleの場合)
これにより、ヒントを使用せずにその列のインデックスを利用しながら、日付のあるすべての行が返され、null値が省略されます。
その列にインデックスを作成します。
インデックスが使用されていることを確認するには、インデックスとwhereの他の列にある必要があります。
ocdecioは答えました:
Select *を実行している場合は、インデックスを使用するのではなく、テーブルスキャンを実行する方が理にかなっています。
それは厳密には真実ではありません。 where句に適合するインデックスがあり、クエリオプティマイザがそのインデックスの使用がテーブルスキャンを実行するよりも高速であると判断した場合、インデックスが使用されます。インデックスがない場合、または適切なインデックスがない場合にのみ、テーブルスキャンを実行する必要があります。