テストデータベースに25万行のテーブルがあります。(本番環境には数億個あります。同じ問題があります。)テーブルには、nvarchar2(50)文字列識別子があり、nullではなく、一意のインデックスが付いています(PKではありません)。
識別子は、テストデータベースに8つの異なる値(および運用中に約1000)を持つ最初の部分、@記号、最後に1〜6桁の数字で構成されます。たとえば、「ABCD_BGX1741F_2006_13_20110808.xml @」で始まる5万行があり、その後に5万の異なる数字が続く場合があります。
識別子に基づいて単一の行を照会すると、カーディナリティは1と推定され、コストは非常に低く、正常に機能します。IN式またはOR式で複数の識別子を使用して複数の行を照会すると、インデックスの推定が完全に間違っているため、テーブル全体のスキャンが使用されます。ヒントを使用してインデックスを強制すると、非常に高速になります。実際には、テーブル全体のスキャンが1桁遅く実行されます(運用環境でははるかに遅くなります)。それはオプティマイザーの問題です。
テストとして、まったく同じDDLとまったく同じコンテンツを使用してテーブル(同じスキーマ+テーブルスペース)を複製しました。適切な測定のために最初のテーブルに一意のインデックスを再作成し、クローンテーブルにまったく同じインデックスを作成しました。私はDBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);
。インデックス名が連続していることもわかります。したがって、2つのテーブルの唯一の違いは、最初のテーブルが長期間にわたってランダムな順序でロードされ、ブロックがディスクに(他のいくつかの大きなテーブルと一緒にテーブルスペースで)散らばっていることです。 INSERT-SELECT。それ以外、違いは想像できません。(元のテーブルは最後の大規模な削除以降縮小されており、その後の単一の削除はありません。)
病気のテーブルとクローンテーブルのクエリプランを次に示します(黒いブラシの下の文字列は、画像全体で同じであり、灰色のブラシの下でも同じです)
(この例では、黒のブラシをかけられた識別子で始まる1867行があります。2行クエリは1867 * 2のカーディナリティを生成し、3行クエリは1867 * 3のカーディナリティを生成します。偶然ですが、Oracleは識別子の終わりを気にしていないようです)
この動作の原因は何ですか?本番環境でテーブルを再作成するのは明らかに高価です。
USER_TABLES:http: //i.stack.imgur.com/nDWze.jpg USER_INDEXES:http : //i.stack.imgur.com/DG9um.jpg スキーマとテーブルスペース名のみを変更しました。テーブルとインデックスの名前は、クエリプランのスクリーンショットと同じであることがわかります。