最適な実行計画を選択するためには、オプティマイザが代表的な統計情報を持っている必要があります。代表的な統計情報とは、必ずしも最新の統計情報ではなく、実行計画内の各操作から期待される正しい行数をオプティマイザが判断するのに役立つ一連の統計情報です。

自動統計情報収集タスク
Oracleは、事前定義されたメンテナンスウィンドウ(平日は午後10時から午前2時、週末は午前6時から午前2時)内に、統計情報が欠落しているか古くなっているすべてのデータベースオブジェクトの統計情報を自動的に収集します。このジョブが実行されるメンテナンスウィンドウは、Enterprise ManagerまたはDBMS_SCHEDULERおよびDBMS_AUTO_TASK_ADMINパッケージを使用して変更できます。

すでに確立された統計情報収集手順がある場合、または何らかの理由で自動統計情報収集を無効にしたい場合は、タスク全体を無効にすることができます:

begin
dbms_auto_task_admin.disable(
client_name=>'auto optimizer stats collection',
operation=>null,
window_name=>null);
end;
/

手動統計情報収集
オプティマイザ統計情報を手動で管理する計画の場合、統計情報をいつ収集すべきかを決定する必要があります。自動ジョブと同様に、情報の陳腐化に基づいて収集タイミングを決定するか、環境に新しいデータがロードされたタイミングに基づいて決定できます。基になるデータが大幅に変更されていない場合に統計情報を継続的に再収集することは、システムリソースを不必要に浪費するため推奨されません。データが事前定義されたETLまたはELTジョブ中にのみ環境にロードされる場合、統計情報収集操作はこのプロセスの一部としてスケジュールできます。統計情報メンテナンス戦略の一部として、オンライン統計情報収集と増分統計情報を活用することを試みるべきです。

オンライン統計情報収集
Oracle Database 18cでは、オンライン統計情報収集機能が強化され、CREATE TABLE AS SELECT(CTAS)やINSERT AS SELECT(IAS)などのダイレクトパス・ロード操作と統合されました。この機能により、データロード処理と同時に統計情報を収集できるため、データロード完了後すぐに最新の統計情報を利用可能になります。従来必要だった追加のフルスキャンが不要となることで、システムリソースの効率的な活用が実現します。

オンライン統計情報収集は、ヒストグラムや索引統計情報を収集しません。これらのタイプの統計情報は追加のデータスキャンを必要とし、データロードのパフォーマンスに大きな影響を与える可能性があるためです。基本列統計情報を再収集せずに必要なヒストグラムと索引統計情報を収集するには、DBMS_STATS.GATHER_TABLE_STATSプロシージャを、新しいオプションパラメータをGATHER AUTOに設定して使用します。パフォーマンス上の理由から、GATHER AUTOはテーブル内のすべての行ではなく、行のサンプルを使用してヒストグラムを構築することに注意してください。

NOTES列の「HISTOGRAM_ONLY」は、基本列統計情報を再収集せずにヒストグラムが収集されたことを示します。オンライン統計情報収集が発生したことを確認する方法は2つあります:①実行計画をチェックして、新しい行ソース「OPTIMIZER STATISTICS GATHERING」が計画に出現するかどうかを確認すします。②USER_TAB_COL_STATISTICSテーブルの新しいNOTES列で「STATS_ON_LOAD」ステータスを探します。

オンライン統計情報収集は、ダイレクトパスロード操作のパフォーマンスへの影響を最小限にするように設計されているため、空のオブジェクトにデータがロードされるときにのみ作動します。既存のテーブルの新しいパーティションへのロード時にオンライン統計情報収集を適用するには、拡張構文を使用して明示的にパーティションを指定します。この場合、パーティションレベルの統計情報が作成されますが、グローバルレベル(テーブルレベル)の統計情報は更新されません。パーティション表で増分統計情報が有効になっている場合、データロード操作の一部としてシソーラスが作成されます。

オンライン統計情報収集は、NO_GATHER_OPTIMIZER_STATISTICSヒントを使用して個々のSQL文で無効にできます。

増分統計情報とパーティション交換データロード
パーティション表の統計情報収集は、テーブルレベル(グローバル統計情報)と(サブ)パーティションレベルの両方の統計情報収集で構成されます。パーティション表のINCREMENTALプリファレンスがTRUEに設定され、DBMS_STATS.GATHER_*_STATSパラメータGRANULARITYにGLOBALが含まれ、ESTIMATE_PERCENTがAUTO_SAMPLE_SIZEに設定されている場合、Oracleは、追加または変更されたパーティションのみをスキャンし、テーブル全体をスキャンせずに、すべてのグローバルレベル統計情報を正確に導出します。

増分グローバル統計情報は、テーブル内の各パーティションのシソーラスを保存することで機能します。シノプシスには、そのパーティションとその中の列に関する統計情報が含まれています。パーティションレベルの統計情報と各パーティションのシソーラスを集約することで、グローバルレベルの統計情報を正確に生成できるため、テーブル全体をスキャンする必要がなくなります。 新しいパーティションがテーブルに追加された場合、新しいパーティションの統計情報のみを収集する必要があります。テーブルレベルの統計情報は、新しいパーティションのシソーラスと既存のパーティションのシソーラスを使用して自動的かつ正確に計算されます。

増分統計情報が有効な場合、パーティション統計情報はサブパーティション統計情報から集約されないことに注意してください。

パーティション交換ロードを使用していて増分統計情報を活用したい場合は、非パーティション表でDBMS_STATSテーブルプリファレンスINCREMENTAL_LEVELを設定し、パーティション交換ロードで使用されることを識別させる必要があります。INCREMENTAL_LEVELをTABLE(デフォルトはPARTITION)に設定することで、Oracleはそのテーブルの統計情報が収集されるときに自動的にテーブルレベルのシソーラスを作成します。このテーブルレベルのシソーラスは、交換後にパーティションレベルのシソーラスになります。

ただし、環境にトリクルフィード(少量のデータ継続投入)やオンラントランザクションが多く、少数の行のみを挿入するがこれらの操作が一日中発生する場合、統計情報がいつ古くなるかを判断し、自動統計情報収集タスクをトリガーする必要があります。統計情報が古いかどうかを判断するためにUSER_TAB_STATISTICSのSTALE_STATS列に依存する計画の場合、この情報は毎日1回のみ更新されることに注意する必要があります。テーブルで発生したDMLに関するよりタイムリーな情報が必要な場合は、USER_TAB_MODIFICATIONSを確認する必要があります。このビューには、各テーブルで発生した挿入(INSERTS)、更新(UPDATES)、削除(DELETES)の回数、テーブルが切り捨てられたかどうか(TRUNCATED列)がリストされており、自身で古さを計算できます。繰り返しますが、この情報はメモリから定期的に自動更新されることに留意してください。最新の情報が必要な場合は、DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO関数を使用して手動で情報をフラッシュする必要があります。

「範囲外(Out of Range)」状態の防止
自動統計情報収集タスクを使用するか手動で統計情報を収集するかにかかわらず、エンドユーザーが統計情報が収集される前に新しく挿入されたデータをクエリし始めると、たとえテーブルの変更行が10%未満であっても、古い統計情報が原因で次善の実行計画が得られる可能性があります。この最も一般的なケースの1つは、WHERE句の述語で指定された値が、[最小値、最大値]列統計情報で表される値の範囲外にある場合です。これは一般的に「範囲外(out-of-range)」エラーとして知られています。この場合、オプティマイザは、述語の値と最大値(値が最大値より高いと仮定)の間の距離に基づいて選択性を比例配分します。つまり、値が最大値または最小値から離れるほど、選択性は低くなります。

このシナリオは、範囲パーティション表で非常に一般的です。既存の範囲パーティション表に新しいパーティションが追加され、行がそのパーティションのみに挿入されます。エンドユーザーは、この新しいパーティションの統計情報が収集される前に、この新しいデータのクエリを開始します。パーティション表の場合、DBMS_STATS.COPY_TABLE_STATSプロシージャ(Oracle Database 10.2.0.4以降で利用可能)を使用して「範囲外」状態を防止できます。このプロシージャは、代表的なソース[サブ]パーティションの統計情報を、新しく作成された空の宛先[サブ]パーティションにコピーします。また、依存オブジェクト(列、ローカル(パーティション)索引など)の統計情報もコピーし、パーティション列の最大値として上限パーティショニング値を、パーティション列の最小値として前のパーティションの上限パーティショニング値を設定します。コピーされた統計情報は、パーティションの正確な統計情報を収集できるようになるまでの一時的な解決策と考えるべきです。統計情報のコピーは、実際に統計情報を収集する代わりとして使用すべきではありません。

デフォルトでは、DBMS_STATS.COPY_TABLE_STATSはパーティション統計情報のみを調整し、グローバルまたはテーブルレベルの統計情報は調整しないことに注意してください。コピーの一部としてパーティション列のグローバルレベル統計情報を更新したい場合は、DBMS_STATS.COPY_TABLE_STATSのflagsパラメータを8に設定する必要があります。

非パーティション表の場合、DBMS_STATS.SET_COLUMN_STATSプロシージャを使用して列の最大値を手動で設定できます。このアプローチは一般的に推奨されておらず、実際に統計情報を収集する代わりにはなりません。