ヘッダーをスキップ

Oracle Database パフォーマンス・チューニング・ガイド
10gリリース2(10.2)

B19207-02
目次
目次
索引
索引

戻る 次へ

11 SQLチューニングの概要

この章では、チューニングの目的、多くのリソースを消費するSQL文の識別方法および収集する内容の説明と、チューニングの提案を示します。

この章には次の項があります。

SQLチューニングの概要

SQL文のチューニングは、データベース・システムのパフォーマンス・チューニングの重要な側面です。SQLチューニングには、次の3つの基本手順を実行します。

システム・パフォーマンスが十分なレベルに達するか、他にチューニングできる文がなくなるまで、この3つの手順を繰り返します。

チューニングの目的

システムをチューニングする目的は、システムのエンド・ユーザーへの応答時間を短縮したり、同じ作業の処理に使用されるリソースを削減することです。これには、次の方法があります。

ワークロードの削減

一般に、SQLチューニングには、同じワークロードをより効率的に処理する方法を見つけ出すことが含まれます。機能性を変えることなく文の実行計画を変更し、リソース使用量を削減することは可能です。

リソース使用量を削減する方法の2つの例を、次に示します。

  1. 一般に実行される問合せで、アクセスするデータの表中での割合が少ない場合、効率的な問合せの実行方法として、索引の使用があります。索引を作成すれば、使用するリソースの量を削減できます。

  2. ユーザーが、特定のソート順序で戻される10,000行の最初の20行を見る場合でかつ、索引で問合せ(およびソート順序)を満たすことができる場合、最初の20行を見るために、10,000行にアクセスしてソートする必要はありません。

ワークロードの均衡化

システムは、実ユーザーがシステムに接続している昼間に使用量が最大に達し、夜間には低下する傾向があります。重要でないレポートやバッチ・ジョブを夜間に実行するようにスケジューリングし、昼間の同時実行性が削減されれば、昼間の、より重要なプログラムのためにリソースが解放されます。

ワークロードのパラレル化

大量のデータにアクセスする問合せ(代表的なものは、データ・ウェアハウス問合せ)は、多くの場合、パラレル化できます。これは特に、同時実行性が低いデータ・ウェアハウスで応答時間を短縮する場合に有効です。ただし、同時実行性が高い傾向のあるOLTP環境の場合は、プログラム全体のリソース使用量を増加させることになり、他のユーザーに影響を与える可能性があります。

高負荷SQLの識別

この項では、高負荷SQL文について、識別およびデータ収集を行う手順を説明します。高負荷のSQLとは、Oracleデータベースのパフォーマンスに影響を与える、パフォーマンスの低いリソース集中型のSQL文です。高負荷SQL文は、次の手段で識別できます。

多くのリソースを消費するSQLの識別

リソース集中型のSQLを識別する最初のステップは、検討する問題の分類です。

特定のプログラムのチューニング

特定のプログラム(GUIまたは3GL)をチューニングする場合、検討するSQLの識別は、プログラム内で実行されたSQLを見るだけの簡単な作業です。Oracle Enterprise Managerに用意されているツールを使用して、リソースを多く使用するSQL文の識別、EXPLAIN PLANの生成およびSQLパフォーマンスの評価ができます。

関連項目:

  • SQLアプリケーションの監視とチューニングの機能の詳細は、『Oracle Enterprise Manager概要』 を参照してください。

  • 自動SQLチューニング機能については、第12章「自動SQLチューニング」を参照してください。

 

SQLを識別できない(たとえば、SQLが動的に生成される)場合は、SQL_TRACEを使用して、実行されたSQLを含むトレース・ファイルを生成し、次にTKPROFを使用して出力ファイルを生成します。

TKPROF出力ファイル内のSQL文は、実行経過時間(exeela)などの各種パラメータで順序付けできるため、通常は、SQL文を経過時間で順序付けする(経過時間が最も長いSQL文をファイルの最も上に置く)ことで識別に利用されます。これにより、ファイル内にSQL文が多数ある場合に、パフォーマンスの低いSQLを識別するジョブの実行が容易になります。

関連項目:

第20章「アプリケーション・トレース・ツールの使用方法」 

アプリケーションのチューニング/負荷の軽減

アプリケーション全体のパフォーマンスが十分に最適化されていない場合や、データベース・サーバーのCPUまたはI/O全体の負荷を減らそうとする場合は、次の手順で、多くのリソースを消費するSQLを識別します。

  1. 検討する時間帯を判別します。通常は、アプリケーションの処理のピーク時間です。

  2. その期間の開始時と終了時にオペレーティング・システム統計およびOracle統計を収集します。収集する最小限のOracle統計は、ファイルI/O(V$FILESTAT)、システム統計(V$SYSSTAT)、およびSQL統計(V$SQLAREAV$SQLまたはV$SQLSTATSV$SQLTEXTV$SQL_PLANおよびV$SQL_PLAN_STATISTICS)です。

    関連項目:

    Oracleツールを使用してOracleインスタンス・パフォーマンス・データを収集する方法の詳細は、第6章「自動パフォーマンス診断」を参照してください。 

  3. ステップ2で収集したデータを使用して、多くのリソースを使用するSQL文を識別します。候補のSQL文を識別するには、V$SQLSTATSを問い合せる方法が適しています。V$SQLSTATSには、共有プール内のすべてのSQL文に関するリソース使用率の情報が含まれています。V$SQLSTATS内のデータを、リソース使用率で順序付けしてください。共通リソースの主なものは、次のとおりです。

    • バッファ取得(V$SQLSTATS.BUFFER_GETS。CPU使用率の高い文の問合せ。)

    • ディスク読取り(V$SQLSTATS.DISK_READS。I/O使用率の高い文の問合せ。)

    • ソート(V$SQLSTATS.SORTS。ソートの多い文の問合せ。)

負荷の最も大きいSQL文を識別する方法の1つは、その期間内にSQL文で使用されたリソースを、同じ期間内に使用されたそのリソースの総量と比較することです。BUFFER_GETSの場合、各SQL文のBUFFER_GETSの回数を、期間中のバッファ取得の総数で除算します。システム内のバッファ取得の総数はV$SYSSTAT表のsession logical readsの統計情報からわかります。

同様に、V$SQL_STATS.DISK_READSV$SYSSTAT統計の物理読込みの値で除算すると、システムによって実行されるディスク読取りの総数のうち、文によって実行されるディスク読取りの割合を計算できます。自動ワークロード・リポジトリ・レポートのSQLセクションにはこのデータが含まれているため、割合を手動で計算する必要はありません。

関連項目:

動的パフォーマンス・ビューの詳細は、『Oracle Databaseリファレンス』を参照してください。 

候補のSQL文を識別した後、文を調べるために必要な情報を収集し、チューニングします。

識別したSQLに関するデータの収集

CPUが問題となっている場合は、一定期間内に最も多くのBUFFER_GETSを実行した上位のSQL文を調べます。その他の場合は、最も多くのDISK_READSを実行したSQL文から始めます。

チューニング中に収集する情報

チューニング・プロセスではまず、基礎となる表と索引の構造を判別します。収集する情報は、次のとおりです。

  1. V$SQLTEXTからの完全なSQLテキスト

  2. SQL文で参照される表の構造(通常はSQL*Plusの表を記述)

  3. すべての索引(列、列の順序付け)の定義と、各索引が一意かどうか

  4. セグメントのオプティマイザ統計(表ごとの行数、索引列の選択性など)、およびセグメントが最後に分析された日付

  5. SQL文で参照されるビューの定義

  6. ステップ5で検出された、ビュー定義で参照されている表について、ステップ2、3および4を繰り返します。

  7. SQL文(EXPLAIN PLANV$SQL_PLANまたはTKPROF出力のいずれかからのもの)のオプティマイザ計画の安定性

  8. そのSQL文の以前のオプティマイザ計画の安定性


    注意:

    アプリケーション内の主要なSQL文すべてについて、実行計画を生成し、見直すことが重要です。これにより、SQL文が効率よく実行されたときのオプティマイザの実行計画と、そうでないときの計画とを比較できます。データ量の変化などの情報とあわせて比較を行うと、パフォーマンスの低下の原因を正確に識別できます。 


自動SQLチューニング機能

手動SQLチューニング・プロセスはアプリケーション開発者に多数の作業が課されるため、SQLチューニング・プロセスは自動SQLチューニング管理機能により自動化されています。これらの機能は、OLTPタイプとデータ・ウェアハウス・タイプのアプリケーションで同様に機能するように設計されています。第12章「自動SQLチューニング」を参照してください。

ADDM

Automatic Database Diagnostic Monitor(ADDM)では、高負荷SQL文など、Oracleデータベースにおいて考えられるパフォーマンス上の問題について、AWRによって収集された情報が分析されます。「Automatic Database Diagnostic Monitor」を参照してください。

SQLチューニング・アドバイザ

SQLチューニング・アドバイザでは、SQL文を変更せずにSQL文を素早く効率的に最適化できます。「SQLチューニング・アドバイザ」を参照してください。

SQLチューニング・セット

複数のSQL文がADDMまたはSQLチューニング・アドバイザへの入力として使用される場合、SQLチューニング・セット(STS)が構成され、格納されます。STSには、SQL文のセットと、関連する実行コンテキストおよび基本実行統計が含まれます。「SQLチューニング・セット」を参照してください。

SQLアクセス・アドバイザ

Oracleでは、SQLチューニング・アドバイザの他に、SQLアクセス・アドバイザが用意されています。これは、マテリアライズド・ビュー、索引およびマテリアライズド・ビュー・ログについてアドバイスを提供します。SQLアクセス・アドバイザでは、指定のワークロードに関するマテリアライズド・ビュー、マテリアライズド・ビュー・ログおよび索引の適切なセットが推奨されるため、パフォーマンスの目標を達成するのに役立ちます。一般に、マテリアライズド・ビューおよび索引の数と、これらに割り当てられている領域が増えるにつれて、問合せのパフォーマンスが向上します。SQLアクセス・アドバイザでは、領域使用量と問合せパフォーマンスの兼合いが考慮され、新規および既存のマテリアライズド・ビューおよび索引の最もコスト効率の高い構成が推奨されます。

Oracle Enterprise Manager Database ControlからSQLアクセス・アドバイザにアクセスする手順は、次のとおりです。

「SQLアクセス・アドバイザの使用方法」を参照してください。

効率的なSQL文の開発

この項では、SQL文の効率を高める方法を説明します。

オプティマイザ統計の確認

問合せオプティマイザでは、最適な実行計画の判別時に、表と索引について収集された統計を使用します。これらの統計が収集されなかった場合、または、統計がデータベース内に格納されているデータをすでに反映しなくなっている場合、オプティマイザには最適な計画を生成するための十分な情報がありません。

チェックする内容

実行計画の検討

OLTP環境でSQL文をチューニング(または作成)する場合、最も選択性の高いフィルタを持つ表から駆動することを目標とします。つまり、次のステップに渡される行を少なくするということです。次のステップが結合である場合は、少数の行しか結合されないということになります。アクセス・パスが最適かどうかを確認してください。

オプティマイザの実行計画を調べる場合は、次の内容を確認します。

これらの条件のうち最適でないものがある場合は、SQL文の再構成や、表で使用できる索引について考慮します。

SQL文の再構成

非効率的なSQL文は、修正するよりも書き直す方が簡単なことがよくあります。元の文の意図を理解していれば、要件を満たす新しい文を迅速かつ容易に作成できます。

ANDと=を使用した条件の組立て

SQLの効率性を高めるには、可能なかぎり等価結合を使用します。変換されない列値に対して等価結合を実行する文は、最も容易にチューニングできます。

WHERE句での変換列の回避

変換されない列値を使用します。たとえば、次の例のように使用します。

WHERE a.order_no = b.order_no

次の例は使用しません。

WHERE TO_NUMBER (SUBSTR(a.order_no, INSTR(b.order_no, '.') - 1))
= TO_NUMBER (SUBSTR(a.order_no, INSTR(b.order_no, '.') - 1))

述語句またはWHERE句では、SQLファンクションを使用しないでください。列を引数として持つ関数など、列を使用する式は、使用できる定義済ファンクション索引がないかぎり、その列の索引を使用できる可能性をオプティマイザが無視する原因となります(一意索引も例外ではありません)。

型が混在する式は避け、暗黙的な型変換に注意してください。VARCHAR2charcolの索引を使用するときに、WHERE句が次のようであるとします。

AND charcol = numexpr

numexprは数値型の式(たとえば、1、USERENV('SESSIONID')、numcolnumcol+0、...)であり、Oracleはこの式を次のように変換します。

AND TO_NUMBER(charcol) = numexpr

次に示すタイプの複合式は使用しないようにしてください。

ここに示した式によって、オプティマイザは有効なカーディナリティまたは選択性の見積りを割り当てることができなくなります。この結果、全体の計画および結合方法に悪い影響を与えます。

NVL()のかわりに述語を追加します。

たとえば、次のような影響があります。

SELECT employee_num, full_name Name, employee_id 
  FROM mtl_employees_current_view 
  WHERE (employee_num = NVL (:b1,employee_num)) AND (organization_id=:1) 
  ORDER BY employee_num;

これは次のようにします。

SELECT employee_num, full_name Name, employee_id 
  FROM mtl_employees_current_view 
  WHERE (employee_num = :b1) AND (organization_id=:1) 
  ORDER BY employee_num;

フィルタまたは述語結合でSQLファンクションを使用する必要がある場合、索引を持つ列に対しては使用しないでください。そのかわり、次の文に示すように、述語の反対側にSQLファンクションを使用してください。

TO_CHAR(numcol) = varcol

次の例は使用しません。

varcol = TO_CHAR(numcol)

関連項目:

ファンクション索引の詳細は、第15章「索引およびクラスタの使用方法」を参照してください。 

特定のタスクに対する専用のSQL文の作成

SQLは、手続き型言語ではありません。1つのSQLを使用して様々なことを実行すると、通常は各タスクに最適でない結果が生じます。SQLを使用して様々なタスクを実行する場合は、1つの文にパラメータを指定して異なるタスクを実行するのではなく、様々な文を作成してください。


注意:

Oracle FormsとOracle Reportsは、PL/SQL(トリガーまたはプログラム・ユニット)を使用してアプリケーション・ロジックをコード化するための強力な開発ツールです。FormsまたはReportsで複雑なロジックを処理することによって、SQL文の複雑さを減らすことができます。また、規模の大きな単一の複雑なSQL文のかわりに、少数のSQL文を実行するサーバー側のPL/SQLパッケージを起動することもできます。このパッケージはサーバー側のユニットであるため、クライアントとデータベースの間のラウンドトリップやネットワークの通信量の問題は発生しません。  


通常、異なるタスクには個別のSQL文を作成することが適していますが、使用するSQL文を1つにする必要がある場合は、UNION ALL演算子を使用することによって、非常に複雑な文を多少簡略化できます。

最適化(実行計画の決定)は、どの値で問合せが置換されるかをデータベースが認識する前に行われます。したがって、実行計画はそれらの値が何であるかに依存しません。 たとえば、次のような場合があります。

SELECT info 
FROM tables
WHERE ... 
AND somecolumn BETWEEN DECODE(:loval, 'ALL', somecolumn, :loval)
AND DECODE(:hival, 'ALL', somecolumn, :hival);

この例では、データベースはsomecolumn列に対して索引を使用できません。この列を含む式が、BETWEENの両辺で同じ列を使用するためです。

このことは、選択性の高い、索引作成可能な他の条件が別にあって、それを使用して駆動表にアクセスできる場合には問題になりません。ただし、これにあてはまらない場合もよくあります。この例のような条件で索引を使用することは多くありますが、:lovalなどの値を、あらかじめ知っておく必要があります。この情報があれば、索引を使用できないALLのケースを除外できます。

:lovalと:hivalに実際の値が指定されている場合には必ず索引を使用する場合(:lovalと:hivalの間が狭く、多くの場合等しいことが期待できる場合)は、この例を論理的に等しい、次の形式にリライトできます。

SELECT /* change this half of UNION ALL if other half changes */ info
FROM tables 
WHERE ... 
AND somecolumn BETWEEN :loval AND :hival
AND (:hival != 'ALL' AND :loval != 'ALL') 
UNION ALL 
SELECT /* Change this half of UNION ALL if other half changes. */ info
FROM tables
WHERE ... 
AND (:hival = 'ALL' OR :loval = 'ALL');

この新しい問合せでEXPLAIN PLANを実行した場合、望ましい実行計画と望ましくない実行計画の両方が得られるように思われます。しかし、データベースがUNION ALLの前半と後半のどちらを実行するかを決めるために最初に評価する条件は、:hival:lovalALLであるかどうかの複合条件です。データベースは、問合せの前半と後半のどちらかの実行計画から実際に行を取得する前に、この条件を評価します。

UNION ALL問合せの一方に関して条件がfalseであれば、その部分はそれ以上評価されません。与えられている値に関して最適な実行計画の部分のみが実際に実行されます。:hival:lovalに関する最終条件はどちらか一方のみがtrueであることが保証されているので、実際に行を戻すのはUNION ALLの半分のみです。(UNION ALL内のALLは、この排他性により論理的に有効です。これにより、問合せの両半分の結果から重複行を除外するためにコストの高いソートを実行することなく、計画を実行できます。)

副問合せに対するEXISTSとINの使用

ある環境では、EXISTSよりINを使用した方が適していることがあります。一般に、選択的述語が副問合せにある場合は、INを使用します。選択的述語が親問合せの中にある場合は、EXISTSを使用します。


注意:

この説明は、親SQLまたは副問合せへのアクセス・パスが選択性の高い索引付きの列を経由するようなOLTP環境で最も当てはまります。DSS環境では、親SQLまたは副問合せの選択性が低い場合があり、結合列には索引がない可能性もあります。DSS環境では、EXISTSの場合にセミ結合を使用することを考慮してください。  


関連項目:

『Oracle Databaseデータ・ウェアハウス・ガイド』 

副問合せにIN句を使用した場合に、副問合せで指定される選択性の利点を活用するために、Oracleが副問合せをリライトすることがあります。これは、最も選択性の高いフィルタが副問合せにある場合、結合列に索引がある場合に、最も有効です。これに対し、EXISTSは、最も選択性の高いフィルタが親問合せにある場合に有効です。その場合、EXISTS基準に照らして行をフィルタにかける前に、親問合せの選択的述語を適用できるからです。


注意:

使用したリソース(BUFFER_GETSDISK_READSV$SQLSTATSまたはV$SQLAREAからのCPU_TIME)の実際の数で、文のオプティマイザ・コストを検証する必要があります。データの偏り(ヒストグラムを使用しない)などの状況は、オプティマイザの操作コストの見積りにマイナスの影響を与える可能性があります。 


「例1: INの使用: 副問合せ内の選択的フィルタ」および「例2: EXISTSの使用: 親の中の選択的述語」に、INおよびEXISTSの利点を実証する2つの例を示します。いずれの例でも、次の特性を持つ同じスキーマを使用します。

例1: INの使用: 副問合せ内の選択的フィルタ

この例は、INを使用するように問合せをリライトすると、パフォーマンスがどのように向上するかを示しています。この問合せでは、顧客144のオーダーを発注したすべての社員を識別します。

EXISTSを使用するSQL文

SELECT /* EXISTS example */
         e.employee_id, e.first_name, e.last_name, e.salary
  FROM employees e
 WHERE EXISTS (SELECT 1 FROM orders o                  /* Note 1 */
                  WHERE e.employee_id = o.sales_rep_id   /* Note 2 */
                    AND o.customer_id = 144);            /* Note 3 */


注意:

  • Note 1: EXISTSを含む行です。

  • Note 2: 副問合せを相関副問合せにする行です。

  • Note 3: 相関副問合せに選択性の高い述語customer_id = numberが含まれている行です。

 

次の計画出力は、前述の文の実行計画(V$SQL_PLANからのもの)です。この計画では、employees表の全表スキャンを必要とし、そのため、多数の行が戻されます。次に、戻された各行がorders表で(索引を経由して)フィルタにかけられます。

  ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
---- -------------------- --------------- ---------------------- --- ----------
   0 SELECT STATEMENT                                            CHO
   1  FILTER
   2   TABLE ACCESS       FULL            EMPLOYEES              ANA        155
   3   TABLE ACCESS       BY INDEX ROWID  ORDERS                 ANA          3
   4    INDEX             RANGE SCAN      ORD_CUSTOMER_IX        ANA          1

INを使用して文をリライトすると、使用されるリソースが大幅に減少します。

INを使用するSQL文

  SELECT /* IN example */
         e.employee_id, e.first_name, e.last_name, e.salary
    FROM employees e
   WHERE e.employee_id IN (SELECT o.sales_rep_id         /* Note 4 */
                             FROM orders o
                            WHERE o.customer_id = 144);  /* Note 3 */


注意:

  • Note 3: 相関副問合せに選択性の高い述語customer_id = numberが含まれている行です。

  • Note 4: INが使用されている行です。副問合せは、相関ではなくなっています。これは、IN句が副問合せ内の結合を置換するためです。

 

次の計画出力は、前述の文の実行計画(V$SQL_PLANからのもの)です。オプティマイザは副問合せをビューにリライトし、次にそれが一意索引でemployees表に結合されます。この結果、計画は大幅に改善されます。ビュー(すなわち、副問合せ)に選択的述語があるため、わずかなemployee_idのみが戻されるためです。次に、このわずかなemployee_idで、一意索引からemployees表にアクセスします。

  ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
---- -------------------- --------------- ---------------------- --- ----------
   0 SELECT STATEMENT                                            CHO
   1  NESTED LOOPS                                                            5
   2   VIEW                                                                   3
   3    SORT              UNIQUE                                              3
   4     TABLE ACCESS     FULL            ORDERS                 ANA          1
   5   TABLE ACCESS       BY INDEX ROWID  EMPLOYEES              ANA          1
   6    INDEX             UNIQUE SCAN     EMP_EMP_ID_PK          ANA
例2: EXISTSの使用: 親の中の選択的述語

この例は、EXISTSを使用するように問合せをリライトすると、パフォーマンスがどのように向上するかを示しています。この問合せでは、オーダーを発注した、部門80の全営業社員を識別します。

INを使用するSQL文

  SELECT /* IN example */
         e.employee_id, e.first_name, e.last_name, e.department_id, e.salary
    FROM employees e
   WHERE e.department_id = 80                                    /* Note 5 */
     AND e.job_id        = 'SA_REP'                              /* Note 6 */
     AND e.employee_id IN (SELECT o.sales_rep_id FROM orders o); /* Note 4 */


注意:

  • Note 4: INが使用されている行です。副問合せは、相関ではなくなっています。これは、IN句が副問合せ内の結合を置換するためです。

  • Note 5および6: 親SQL内に選択的述語があります。

 

次の計画出力は、前述の文の実行計画(V$SQL_PLANからのもの)です。SQL文は、orders表でビューを使用するようにオプティマイザでリライトされています。この文では、orders表に存在するすべての一意のemployee_idを戻すためにデータのソートが必要です。述語がないため、多数のemployee_idsが戻されます。この多数のemployee_idからなる大きなリストが、一意索引を使用してemployees表へのアクセスに使用されることになります。

  ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
---- -------------------- --------------- ---------------------- --- ----------
   0 SELECT STATEMENT                                            CHO
   1  NESTED LOOPS                                                          125
   2   VIEW                                                                 116
   3    SORT              UNIQUE                                            116
   4     TABLE ACCESS     FULL            ORDERS                 ANA         40
   5   TABLE ACCESS       BY INDEX ROWID  EMPLOYEES              ANA          1
   6    INDEX             UNIQUE SCAN     EMP_EMP_ID_PK          ANA

EXISTSを使用するSQL文

  SELECT /* EXISTS example */
         e.employee_id, e.first_name, e.last_name, e.salary
    FROM employees e
   WHERE e.department_id = 80                           /* Note 5 */
     AND e.job_id        = 'SA_REP'                     /* Note 6 */
     AND EXISTS (SELECT 1                               /* Note 1 */
                   FROM orders o
                  WHERE e.employee_id = o.sales_rep_id);  /* Note 2 */


注意:

  • Note 1: EXISTSを含む行です。

  • Note 2: 副問合せを相関副問合せにする行です。

  • Note 5および6: 親SQL内に選択的述語があります。

 

次の計画出力は、前述の文の実行計画(V$SQL_PLANからのもの)です。計画のコストは、EXISTSを使用するようにSQL文をリライトすることで削減されます。この計画は、より効率的です。これは、2つの索引を使用して親問合せ内の述語を満たすため、戻されるemployee_idがきわめて少ないためです。次に、このemployee_idを使用して、索引からorders表にアクセスします。

  ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
---- -------------------- --------------- ---------------------- --- ----------
   0 SELECT STATEMENT                                            CHO
   1  FILTER
   2   TABLE ACCESS       BY INDEX ROWID  EMPLOYEES              ANA         98
   3    AND-EQUAL
   4     INDEX            RANGE SCAN      EMP_JOB_IX             ANA
   5     INDEX            RANGE SCAN      EMP_DEPARTMENT_IX      ANA
   6   INDEX              RANGE SCAN      ORD_SALES_REP_IX       ANA          8


注意:

より効果的なアプローチは、department_idおよびjob_idの連結索引を作成することです。これによって2つの索引にアクセスする必要がなくなり、使用されるリソースが削減されます。 


ヒントによるアクセス・パスおよび結合順序の制御

オプティマイザのアプローチと目標の設定および問合せオプティマイザの代表的な統計の収集によって、オプティマイザの選択を変えることができます。特定のアプリケーション・データに関して、オプティマイザよりも多くの情報を持つアプリケーション設計者であれば、より効率よくSQL文を実行する方法を選択できる場合があります。SQL文のヒントを使用すれば、文を実行する方法をオプティマイザに指示できます。

/*+FULL */などのヒントは、アクセス・パスを制御します。 たとえば、次のようにします。

SELECT /*+ FULL(e) */ e.last_name
  FROM employees e
 WHERE e.job_id = 'CLERK';

関連項目:

第13章「問合せオプティマイザ」および第16章「オプティマイザ・ヒントの使用方法」 

結合順序は、パフォーマンスに大きな影響を与えることがあります。SQLのチューニングの主な目的は、結果に影響しない不要な行にアクセスする作業を回避することです。このことから次の3つの一般ルールが導かれます。

次の例は、結合順序を効果的にチューニングする方法を示しています。

SELECT info
FROM taba a, tabb b, tabc c
WHERE a.acol BETWEEN 100 AND 200
AND b.bcol BETWEEN 10000 AND 20000
AND c.ccol BETWEEN 10000 AND 20000
AND a.key1 = b.key1
AND a.key2 = c.key2;

  1. 駆動表と駆動索引(存在する場合)を選択します。

    前述の例における最初の3つの条件は、それぞれ1つの表にのみ適用されるフィルタ条件です。最後の2つの条件は結合条件です。

    フィルタ条件は、駆動表と駆動索引の選択を左右します。一般に、表内の排除される部分の比率が最も高くなるフィルタ条件を含む表は駆動表です。この例では、100〜200の範囲はacolの範囲に比べて狭く、10000〜20000の範囲は相対的に大きいため、tabaは駆動表であり、他はすべて同じです。

    ネステッド・ループ結合の場合、結合はすべて結合索引を介して行う必要があります。この結合索引は、主キーまたは外部キーに付けられているもので、その表を結合ツリー内のそれより前にある表に結びつけるために使用します。駆動表を除いて、非結合条件にこの結合索引を使用することはほとんどありません。そのため、tabaを駆動表として選択した後は、b.key1c.key2の索引を使用してtabbtabcを駆動します。

  2. 未使用の最適なフィルタを最初に駆動する最適な結合順序を選択します。

    最適な未使用フィルタを持つ表に先に結合することでその後の結合の作業は、削減できます。したがって、「bcol BETWEEN ...」が「ccol between ...」よりも限定的な(行をより高い比率で排除する)場合は、tabbtabcよりも前に結合されると、最後の結合はより簡単に(より少ない行で)実行できます。

  3. ORDEREDまたはSTARヒントを使用して、結合順序を強制的に設定できます。

    関連項目:

    「結合順序のヒント」 

ビューを管理するときの注意

ビューの結合、ビューへの外部結合、および既存ビューの新規目的への再利用に対しては、注意が必要です。

複合ビューを結合するときの注意

複合ビューへの結合、特に、ある複合ビューから別の複合ビューへの結合はお薦めできません。そのような結合を行うと、多くの場合、ビュー全体がインスタンス化され、ビュー・データに対して問合せが行われる結果になります。

たとえば、次の文は従業員および部門をリストするビューを作成します。

CREATE OR REPLACE VIEW emp_dept
AS
SELECT d.department_id, d.department_name, d.location_id,
     e.employee_id, e.last_name, e.first_name, e.salary, e.job_id
FROM  departments d
     ,employees e
WHERE e.department_id (+) = d.department_id;

次の問合せは指定した状態の従業員を検索します。

SELECT v.last_name, v.first_name, l.state_province
  FROM locations l, emp_dept v
 WHERE l.state_province = 'California'
  AND   v.location_id = l.location_id (+);

次の計画表出力では、emp_deptビューがインスタンス化されます。

--------------------------------------------------------------------------------
| Operation                 |  Name    |  Rows | Bytes|  Cost  | Pstart| Pstop |
--------------------------------------------------------------------------------
| SELECT STATEMENT          |          |       |      |        |       |       |
|  FILTER                   |          |       |      |        |       |       |
|   NESTED LOOPS OUTER      |          |       |      |        |       |       |
|    VIEW                   |EMP_DEPT  |       |      |        |       |       |
|     NESTED LOOPS OUTER    |          |       |      |        |       |       |
|      TABLE ACCESS FULL    |DEPARTMEN |       |      |        |       |       |
|      TABLE ACCESS BY INDEX|EMPLOYEES |       |      |        |       |       |
|       INDEX RANGE SCAN    |EMP_DEPAR |       |      |        |       |       |
|    TABLE ACCESS BY INDEX R|LOCATIONS |       |      |        |       |       |
|     INDEX UNIQUE SCAN     |LOC_ID_PK |       |      |        |       |       |
--------------------------------------------------------------------------------
ビューの再利用禁止

ある用途のために作成したビューを他の用途に使用することは、不適切な場合があるため注意してください。ビューから問合せを行うと、データを戻すために、そのビューに関連するすべての表がアクセスされます。ビューを再利用する前に、ビュー内のすべての表にアクセスしてデータを戻す必要があるかどうかを判別してください。その必要がない場合は、ビューを使用しないでください。かわりに、実表を使用するか、必要に応じて新しいビューを定義してください。その目的は、必要なデータを戻すために参照する表およびビューの数を最小限にすることにあります。

次の例を見てください。

SELECT department_name 
FROM emp_dept
WHERE department_id = 10;

ビュー全体ではまず、employeesおよびdepartments表の結合によりインスタンス化され、次にデータが集計されます。ただし、department_namedepartment_idは、departments表から直接取得できます。emp_deptビューを問い合せてこの情報を取得することは非効率的です。

副問合せのネストを解除するときの注意

副問合せのネストの解除によって、副問合せ本体が解除され、その副問合せが含まれている文の本体にマージされます。これにより、オプティマイザは、アクセス・パスと結合を評価するときに、副問合せと本体の両方をいっしょに考慮させてしまいます。

関連項目:

副問合せのネスト解除における危険性については、『Oracle Databaseデータ・ウェアハウス・ガイド』を参照してください。 

ビューへの外部結合を実行するときの注意

複数表のビューに対する外部結合の場合、等価述語が定義されていれば、問合せオプティマイザ(リリース8.1.6以上)は外部結合列から駆動できます。

ビュー内での外部結合は、外部結合のパフォーマンスに与える影響が読めないため、問題が発生しやすくなります。

中間結果の格納

中間の表、すなわちステージング表がリレーショナル・データベース・システムで比較的よく利用されるのは、それらの表に中間結果を一時的に格納するためです。これは、多くのアプリケーションで役に立つものですが、作成するにはさらにリソースが必要になります。したがって、これらの表による利益が、作成のコストに見合うものかどうかを常に考慮してください。ステージング表は、その情報が何回も再利用されない場合は、作成しないようにしてください。

他の考慮事項

索引の再構成

多くの場合は、索引を再構成すると、パフォーマンスが向上します。これには、次の内容が含まれます。

索引を万能策として使用しないでください。アプリケーションの開発者は、索引を多く作成すればパフォーマンスが改善されると考えることがあります。1人のプログラマが適切に索引を作成すれば、アプリケーションのパフォーマンスは十分に改善される可能性があります。ただし、50名のプログラマが別々に索引を作成すると、アプリケーションのパフォーマンス改善は望めません。

トリガーおよび制約の変更または無効化

トリガーを使用すると、システムのリソースが消費されます。使用するトリガーが多すぎると、パフォーマンスに悪影響が及ぶので、トリガーを変更または使用禁止にする必要がある場合があります。

データの再構成

索引と文を再構成した後で、データの再構成について検討できます。

実行計画の長期的な保持

格納されている統計またはSQL実行計画を使用すると、SQL文の既存の実行計画を長期的に保持できます。表のオプティマイザの統計を格納すると、その表を参照するすべてのSQL文にその統計が適用されます。実行計画を格納すると(すなわち、プラン・スタビリティ)、単一のSQL文の計画が保持されます。統計およびストアド・プランの両方がSQL文に対して使用可能な場合は、オプティマイザはストアド・プランの方を使用します。

関連項目:

 

データへのアクセスを最小限に削減

アプリケーションから各行へのアクセスを、可能なかぎり1回のみにします。そうすることで、ネットワークの通信量が削減され、データベースの負荷が軽減されます。次を実行することを考慮してください。

CASE文による複数のスキャンの結合

多くの場合、様々な表セットで異なった集計を行う必要があります。通常、この計算は、表に複数のスキャンを行って処理されますが、1回の単一のスキャンですべての集計を計算すると簡単です。n-1回のスキャンを排除することで、パフォーマンスを大幅に向上できます。

複数のスキャンを1つのスキャンに結合するには、各スキャンのWHERE条件の内容をCASE文の中に移動します。CASE文は、集計対象のデータをフィルタにかけます。各集計では、データを取り出す別の列があっても構いません。

次の例では、収入が毎月2000より少ない社員、2000〜4000の社員、4000を超える社員の数を問い合せています。これは、次の3つの問合せで行うことができます。

SELECT COUNT (*)
  FROM employees
  WHERE salary < 2000;

SELECT COUNT (*)
  FROM employees
  WHERE salary BETWEEN 2000 AND 4000;

SELECT COUNT (*)
  FROM employees
  WHERE salary>4000;

しかし、1つの文で問合せ全体を実行する方が効率的です。各数値は1つの列として計算されます。countファンクションは、CASE文によるフィルタを使用して、条件が一致する行のみを数えます。 たとえば、次のような場合があります。

SELECT COUNT (CASE WHEN salary < 2000 
                   THEN 1 ELSE null END) count1, 
       COUNT (CASE WHEN salary BETWEEN 2001 AND 4000 
                   THEN 1 ELSE null END) count2, 
       COUNT (CASE WHEN salary > 4000 
                   THEN 1 ELSE null END) count3 
  FROM employees; 

これは、きわめて単純な例です。範囲が重なっていたり、集計の関数が異なっていることもあります。

RETURNING句を持つDMLの使用

適時、INSERTUPDATEまたはDELETE...RETURNINGを使用して、1回のコールでデータを選択および変更します。この技法は、データベースのコール回数を減らすことでパフォーマンスを改善します。

関連項目:

INSERTUPDATEおよびDELETEの各文の構文については、『Oracle Database SQL言語リファレンス』を参照してください。 

1つの文での必要なすべてのデータの変更

可能であれば、配列処理を使用します。つまり、バインド変数の値の配列が繰返し実行のためにOracleに渡されます。これは、あるセットの複数行が同じ操作の対象である場合のくり返し処理に適しています。

たとえば、次のようにします。

BEGIN
 FOR pos_rec IN (SELECT * 
   FROM order_positions 
   WHERE order_id = :id) LOOP
      DELETE FROM order_positions
      WHERE order_id = pos_rec.order_id AND
        order_position = pos_rec.order_position;
 END LOOP;
 DELETE FROM orders 
 WHERE order_id = :id;
END;

別の方法として、ordersに対するカスケード制約を定義できます。前述の例では、1つのSELECTに対してn個のDELETEが実行されます。orders表に対するDELETE要求として、DELETE FROM orders WHERE order_id = :idに対してDELETEを発行すると、データベースは、1回のDELETE文で複数の行を自動的に削除します。

関連項目:

分散問合せをチューニングする方法の詳細は、『Oracle Database管理者ガイド』または『Oracle Database Heterogeneous Connectivity管理者ガイド』を参照してください。 


戻る 次へ
Oracle
Copyright © 2000, 2008, Oracle Corporation.

All Rights Reserved.
目次
目次
索引
索引