3000K 行のテーブル 'MSATTRIBUTE' があります。次のクエリを使用してデータを取得しました。このクエリには、同じ DB データを使用した異なる実行プランがありますが、env は異なります。ある環境ではフルスキャンのように見えるため、クエリは非常に遅いですが、別の環境ではすべてインデックススキャンを使用していました。これは非常に良いことです。 env 1でテストしたのと同じようにインデックススキャンになります。このクエリを改善するにはどうすればよいですか?
4 に答える
あなたのデータモデルとあなたのビジネスについて私が知りたい以上のことを理解していなければ、具体的な前向きなアドバイスをすることは困難です. ただし、インデックス作成戦略と、オプティマイザーが使用しているインデックスを使用していないと推測する理由について、いくつかのメモを示します。
サブクエリでは、REDLINE_MSATTRIBUTE へのアクセス パスが 3 つの列から駆動されます。
- クラス
- OBJECT_ID
- CHANGE_RELEASE_DATE。
CLASS は索引付けされていません。しかし、それはおそらくあまり選択的ではありません。OBJECT_ID は複合インデックスの先頭の列ですが、他の列はサブクエリとは無関係です。
しかし、最大の問題は CHANGE_RELEASE_DATE です。これはまったく索引付けされていません。これは悪いニュースです。1 つの主キーのルックアップで日付が生成され、それが CHANGE_RELEASE_DATE と比較されます。列がインデックス化されていない場合、データベースはテーブルを読み取ってその値を取得する必要があります。
メインクエリはオフを駆動します
- 属性
- CHANGE_ID
- OBJECT_ID (再度)
- CHANGE_RELEASE_DATE (再度)
- クラス(再び)
- OLD_VALUE
ATTID は索引付けされていますが、その索引はどの程度選択的ですか? オプティマイザーはおそらく、それが非常に選択的であるとは考えていません。ATTID も CHANGE_ID と OLD_VALUE を含む複合インデックスにありますが、いずれも先頭の列ではないため、あまり役に立ちません。CLASS、CHANGE_RELEASE_DATE、および OBJECT_ID については既に説明しました。
オプティマイザーは、テーブル スキャンよりも安価な (読み取りが少ない) 場合にのみ、インデックスの使用を選択します。これは通常、WHERE 句の条件をインデックスの先頭(つまり、一番左) の列にマップする必要があることを意味します。これは、サブクエリの OBJECT_ID と ATTID の場合に当てはまります。
- REDLINE_MSATTRIBUTE_INDEX1 には 2 つの列の間に CHANGE_ID があるため、実行計画は INDEX SKIP SCAN を実行する必要があります。
- CLASS と CHANGE_RELEASE_DATE を取得するには、とにかくデータベースがテーブルにアクセスする必要があります。
そのため、 にインデックスを作成することで、ある程度の改善が得られる場合があります(CHANGE_RELEASE_DATE, CLASS, OBJECT_ID, ATTID)
。しかし、前もって言ったように、あなたの状況について詳しく知らなければ、これらは情報に基づいていない推測にすぎません.
2 つのテーブルで行の順序が異なる場合、2 つのシステムのインデックスのクラスタリング ファクタが異なる可能性があるため、インデックス アクセスの推定コストが異なります。クラスタリング係数を含むテーブルとインデックスの統計をチェックして、大きな違いがあるかどうかを確認します。
また、システムの説明計画のいずれかで、動的サンプリングについて言及されていますか?
オラクルにインデックスがあり、それを使用する/使用しないことを決定した場合、1) OPTIMIZER_MODE の設定が異なる可能性があります - RBO にないことを確認してください。2) データが異なる - この場合、オラクルはクエリ統計を異なる方法で評価する可能性があります。3) データは同じですが、統計は最新ではありません。この場合 - 統計を収集する
dbms_stats.gather_table_stats('your3000Ktable',cascade=>true);
4) オラクルが 1 つの環境でインデックスを使用しない理由は他にもたくさんあります。パラメーターを比較することをお勧めします (OPTIMIZER_ INDEX_COST_ADJ など...)。
当面の問題の 1 つは、この部分 SELECT RELEASE_DATE FROM CHANGE WHERE ID = 136972355 です (このコードは、返されるすべての行に対して実行され、その必要はありません...これを行うより良い方法は、単一のデカルト テーブルを使用することです。一度だけ実行され、比較する静的な値を返します....
例 1:
Select * From Table1, (Select Sysdate As Compare From Dual) Table2 Where Table1.Date > Table2.Compare.
Select * from Table1 Where Date > Sysdate -- Sysdate は動的関数ベースの値であるため、行ごとに呼び出されます。前の例は、1 回リテラルに解決され、劇的に高速になります。これは間違いなく、クエリを傷つけ、テーブルスキャンを強制する一片だと思います。
また、これはクエリを実行するためのより効率的な方法だと思います。
Select
REDLINE_MSATTRIBUTE.ATTID
,REDLINE_MSATTRIBUTE.VALUE
From
REDLINE_MSATTRIBUTE
,(
SELECT ATTID
,CHANGE_ID
,MIN(CHANGE_RELEASE_DATE) RELEASE_DATE
FROM REDLINE_MSATTRIBUTE
,(SELECT RELEASE_DATE FROM CHANGE WHERE ID = 136972355) T_COMPARE
WHERE CLASS = 9000
And OBJECT_ID = 32718015
And CHANGE_RELEASE_DATE > T_COMPARE.RELEASE_DATE
And ATTID IN (1564, 1565)
GROUP
BY ATTID,
CHANGE_ID
) T_DYNAMIC
Where
REDLINE_MSATTRIBUTE.ATTID = T_DYNAMIC.ATTID
And REDLINE_MSATTRIBUTE.CHANGE_ID = T_DYNAMIC.CHANGE_ID
And REDLINE_MSATTRIBUTE.RELEASE_DATE = T_DYNAMIC.RELEASE_DATE
And CLASS = 9000
And OBJECT_ID = 32718015
And OLD_VALUE ='Y'
Order
By REDLINE_MSATTRIBUTE.ATTID,
REDLINE_MSATTRIBUTE.VALUE;