特定の組織が特定の期間に渡した注文の数を数えようとしています。しかし、以下のクエリ(2日間の時間範囲)は、1日に2つの個別のクエリを実行するよりもはるかに遅いことがわかりました。
SELECT COUNT(*) FROM ORDER_HISTORY
WHERE organization = 'BA' AND TIMESTAMP > = TO_DATE('2016-01-05', 'YYYY-MM-DD')
AND TIMESTAMP<= TO_DATE('2016-01-05', 'YYYY-MM-DD')+2;
列timestamps
にインデックスがあり、列organization
に別のインデックスがあります
これが私のテーブルのスキーマです。列timestamp
はタイプDATE
です。
2日間のクエリの実行プランでは、organization
のインデックスを使用します。
1日以上のクエリの実行プランでは、timestamp
のインデックスを使用します。
いくつかの統計を取得するには:
SELECT COUNT(*) FROM ORDER_HISTORY
WHERE ORGANIZATION = 'BA' ;
2359847を与えます。
SELECT COUNT(*) FROM ORDER_HISTORY
WHERE TIMESTAMP > = TO_DATE('2016-01-05', 'YYYY-MM-DD')
AND TIMESTAMP<= TO_DATE('2016-01-05', 'YYYY-MM-DD')+1;
9260を返します。2日間の同じクエリは16510を返します。
Oracle DBエンジンの奇妙な動作が発生するのはなぜですか?
その理由は、コストベースのオプティマイザが以前の選択からメモリ内にプランを持っているためです。この場合、特定の組織のレコード数、つまり「LOL」ははるかに少なく、14000程度でした。プランがメモリ内にある場合、CBOパラメータ値はもう関係ありません。使用する:
WHERE organization || '' = 'BA'
オプティマイザ統計が古くなっているか、データに偏りがある(つまり、ORGANIZATION列に人気があり、人気がない)ようです。オプティマイザーは、フィルターORGANIZATION = 'BA'
が14898行を返すと推定しますが、これは実際の結果とはかなり異なります。 dbms_stats.gather_table_stats
で問題が解決するはずです。 **スクリーンショットによると、「LOL」はEXPLAIN
で使用される組織の価値のようです。実際の実行計画を投稿することをお勧めします。
2つの列(ORGANIZATION、タイムスタンプ)で拡張統計を作成することもできます。
また、explain
の結果ではなく、常に実際の実行計画(dbms_xplan.display_cursor
)を確認してください。
更新
問題の調査に役立つもう1つのこと。また、v$sql
/v$sqlarea
でクエリを検索し、そのコストを確認することをお勧めします。また、v$sql
にはis_bind_aware
列とis_bind_sensitive
列があり、オプティマイザが異なるプランを考慮して使用するかどうかは、バインド変数の異なる値に依存するかどうかを示します。複数のプランが生成された理由に関するその他の詳細V$SQL_SHARED_CURSOR
システムビューは、既存の子カーソルが新しい子カーソルと共有されなかった理由を示します。