4

私はこのテーブルを持っています:

create table demo (
    key number(10) not null,
    type varchar2(3) not null,
    state varchar2(16) not null,
    ... lots more columns ...
)

そしてこのインデックス:

create index demo_x04 on demo(key, type, state);

このクエリを実行すると

select * from demo where key = 1 and type = '003' and state = 'NEW'

EXPLAIN PLANは、完全なテーブル スキャンを実行することを示しています。そのため、インデックスを削除して再度作成しました。EXPLAIN PLANまだフルテーブルスキャンと言っています。それはどうしてですか?

背景: これは履歴データであるため、状態を含む行を検索し、状態CLEAREDを含む新しい行を挿入しますNEW(さらに、古い行からいくつかの値をコピーします)。その後、古い行が に更新されUSEDます。したがって、テーブルは常に大きくなります。私が気付いたのは、インデックスのカーディナリティが 0 であることです (何千もの異なる値があるにもかかわらず)。再作成後、カーディナリティは増加しましたが、CBO はインデックスを気に入りませんでした。

翌朝、オラクルは突然インデックスを気に入り (おそらくそれで寝ていたのでしょう)、それを使い始めましたが、長くは続きませんでした。しばらくすると、処理速度が 50 行/秒から 3 行/秒に低下し、再び「FULL TABLE SCAN」が表示されました。何が起こっている?

私の場合、約 100 万行を処理する必要があります。私は約バッチで変更をコミットします。50. インデックスを更新/再編成するためにコミット後に実行する必要があるコマンドなどはありますか?

私はOracle 10gを使用しています。

[編集] このテーブルには 969,491 個の異なるキーがあり、3 つのタイプと 3 つの状態があります。

4

5 に答える 5

4

インデックスヒントを指定するとどうなりますか? これを試して:

SELECT /*+ INDEX (demo demo_x04) */ * 
  FROM demo 
 WHERE key = 1 
   AND type = '003' 
   AND state = 'NEW';

テーブルが分析されたのは、一晩で起こったことのようです。次に、テーブルに対して処理を実行すると、十分な量のインデックスが更新され、オラクルのテーブルの統計が再び古くなり、オプティマイザはインデックスの使用を停止しました。

ヒントを追加して、EXPLAIN PLAN が別のプランを提供し、クエリのパフォーマンスが向上するかどうかを確認します。

ああ、テーブルの分析に関するトニーの答えは一般的な良い習慣ですが、10g では、データベースはその点で自己保守を行うのにかなり適しています。プロセスが多くの更新を行っている場合、インデックスはすぐに古くなる可能性があります。プロセスが溝に入り始めたときに分析を実行すると、しばらく状況が改善される場合、これが問題であることがわかります。

テーブルの統計を更新するには、dmbs_stats.gather_table_statsパッケージを使用します。

例えば:

exec dbms_stats.gather_table_stats('所有者','DEMO');

于 2009-11-14T15:29:32.203 に答える
3

テーブルは最近分析されましたか?Oracleがそれが非常に小さいと考える場合、インデックスの使用を検討することすらできないかもしれません。

これを試して:

select last_analyzed, num_rows 
from user_tables
where table_name = 'DEMO';

NUM_ROWSは、テーブルに含まれているとOracleが考える行数を示します。

于 2009-11-14T15:00:34.227 に答える
3

「翌朝、オラクルは突然インデックスを気に入りました (おそらくそれで寝ていました)」 おそらく DBMS_STATS が一晩実行されています。

通常、インデックスに対する FULL TABLE SCAN の 3 つの理由のいずれかが表示されます。1 つ目は、テーブルが空であるか、少なくとも非常に小さいとオプティマイザが判断することです。これが最初の問題だったと思います。その場合、インデックスを使用するよりも、少数のブロックのみで構成されるテーブルをフル スキャンする方が高速です。

2 つ目は、クエリがインデックスを実際に使用できないような場合です。

"select * from demo where key = 1 and type = '003' and state = 'NEW'"

クエリでハードコーディングされたリテラルを実際に使用していますか。そうでない場合は、変数のデータ型が正しくない可能性があります (キーが文字であるなど)。その場合、数値キーを比較のために文字に変換する必要があり、インデックスがほとんど役に立たなくなります。

3 番目の理由は、クエリがテーブル内の行の大部分を処理すると考えられる場合です。Type と State はカーディナリティがかなり低いようです。おそらく、特定の「キー」値が多数ありますか?

于 2009-11-15T10:16:50.313 に答える
1

あなたが説明した処理に関するコメント:断続的なコミットで行ごとの処理を行っているように聞こえます。可能であれば、これを再考することをお勧めします。更新/挿入メカニズムは MERGE ステートメントに変換される可能性があり、データ セット全体を 1 つのステートメントで処理し、最後に 1 つのコミットを行うことができます。これはほぼ間違いなく、現在の方法よりも高速で、使用するリソースが少なくなります。

于 2009-11-16T06:02:12.150 に答える
0

列キーの値は常に 1 ですか? もしそうなら、とにかく各行を調べなければならないので、インデックスを調べることでクエリが最適化されるかどうかはわかりません。その場合は、キー列なしでインデックスを宣言します。あなたも試すことができます:

select key, type, state from demo where key = 1 and type = '003' and state = 'NEW'

(私の推測が正しければ)各行を調べる必要がありますが、結果セットのすべての列がカバーされるようになったため、インデックスに移動する可能性があります。

インデックスがカーディナリティ0を示しているというあなたの声明に基づいて推測しています。

于 2009-11-14T14:35:43.903 に答える