不要な解析をなくすには、カーソルを正しく操作することと、次に示すカーソル管理オプションを選択して使用する必要があります。
MAXOPENCURSORS
HOLD_CURSOR
RELEASE_CURSOR
カーソルには、暗黙カーソルと明示カーソルの2種類があることを思い出してください。カーソルは、データ定義文およびDML文のすべてについて暗黙的に宣言されます。ただし、複数行を戻す問合せでは、カーソルを明示的に宣言する(またはホスト配列を使用する)必要があります。DECLARE CURSOR文を使用すると、明示カーソルを宣言できます。明示カーソルのオープンおよびクローズを処理する方法はパフォーマンスに影響します。
アクティブ・セットを再評価する必要がある場合は、そのカーソルを再OPENするのみで済みます。OPENでは任意の新しいホスト変数値が使用されます。カーソルを再OPENする前にCLOSEしなければ、処理時間を節約できます。
注意:
すでにオープンしているカーソルを再OPENすると、パフォーマンスの最適化が簡単になります。ただし、これはANSI拡張機能です。したがって、MODE=ANSIを指定している場合には、カーソルを再OPENする前にCLOSEする必要があります。
カーソルのOPENによって取得したリソース(メモリーおよびロック)を解放するときにのみ、そのカーソルをCLOSEします。たとえば、プログラムでは終了前にすべてのカーソルをCLOSEする必要があります。
一般に、明示的に宣言されたカーソルの制御には、次の3つの方法があります。
DECLARE、OPENおよびCLOSEを使用します。
PREPARE、DECLARE、OPENおよびCLOSEを使用します。
MODE=ANSIの場合は、COMMITによってカーソルをクローズします。
最初の方法を使用する場合は、不要な解析に注意する必要があります。カーソルをCLOSEしたか、まだOPENしていないために、解析された文を使用できないときにかぎり、OPENで解析を実行します。プログラムはカーソルをDECLAREし、ホスト変数の値が変わるたびにこれを再OPENし、このSQL文が必要なくなったときにのみこれをCLOSEする必要があります。
2番目の方法(動的SQL方法3および方法4用の方法)を使用する場合は、PREPAREで解析が実行され、解析された文はCLOSEを実行するまで使用できます。プログラムで次のようにする必要があります。
SQL文をPREPAREします。
カーソルをDECLAREします。
ホスト変数の値が変更されるたびに、カーソルを再OPENします。
SQL文を再びPREPAREします。
SQL文が変更された場合は、カーソルを再OPENします。
SQL文が不要になった場合にのみカーソルをCLOSEします。
OPEN文およびCLOSE文をループの中に配置するのはできるだけ避けてください。SQL文の不要な再解析の原因になります。次の例では、OPEN文とCLOSE文がどちらも外側のwhileループの中にあります。MODE=ANSIの場合は、CLOSE文は例に示す位置に配置する必要があります。ANSIでは、カーソルを再OPENする前にCLOSEする必要があります。
EXEC SQL DECLARE emp_cursor CURSOR FOR SELECT ename, sal from emp where sal > :salary and sal <= :salary + 1000; salary = 0; while (salary < 5000) { EXEC SQL OPEN emp_cursor; while (SQLCODE==0) { EXEC SQL FETCH emp_cursor INTO .... ... } salary += 1000; EXEC SQL CLOSE emp_cursor; }
一方、MODE=ORACLEのときは、カーソルを再OPENせずにCLOSE文を実行できます。CLOSE文を外側のwhileループの外に指定すると、OPEN文が繰り戻されるたびに再解析されるのを回避できます。
... while (salary < 5000) { EXEC SQL OPEN emp_cursor; while (sqlca.sqlcode==0) { EXEC SQL FETCH emp_cursor INTO .... ... } salary += 1000; } EXEC SQL CLOSE emp_cursor;
SQL文の解析は、構成を変更しないかぎり一度のみで十分です。たとえば、選択リストまたはWHERE句に列を追加して問合せの構成を変更するとします。HOLD_CURSORおよびRELEASE_CURSOR、MAXOPENCURSORSオプションによって、サーバーにおけるSQL文の解析および再解析の管理方法を制御できます。明示カーソルを宣言すると、解析を最大限に制御できます。
DML文を実行すると、その文に対応しているカーソルがPro*C/C++カーソル・キャッシュ内のエントリにリンクされます。カーソル・キャッシュとはカーソル管理のために使用されて連続的に更新されるメモリー領域です。カーソル・キャッシュ・エントリは、次々に1つのプライベートSQL領域にリンクされます。
プライベートSQL領域とは、実行時に動的に作成される作業領域で、ホスト変数のアドレスおよびその文の処理に必要なその他の情報が保存されます。明示カーソルを使用すると、SQL文に名前を付け、プライベートSQL領域に保存されている情報にアクセスし、この情報の処理をある程度制御できます。
図B-2は、プログラムでINSERTおよびDELETEを実行した後のカーソル・キャッシュを表しています。
ユーザー・セッションごとのオープン・カーソルの最大数は、初期化パラメータOPEN_CURSORSによって設定します。
MAXOPENCURSORSは、カーソル・キャッシュの初期サイズを指定します。新しいカーソルが必要で、しかも空きのキャッシュ・エントリがない場合、サーバーではエントリの再利用が試行されます。再利用の可能性はHOLD_CURSORとRELEASE_CURSORの値によって決まり、また明示カーソルの場合には、カーソル自体の状態によって決まります。
MAXOPENCURSORSの値が実際に必要なキャッシュ・エントリの数より小さい場合、サーバーでは再利用可能とマークされている最初のキャッシュ・エントリが使用されます。たとえば、INSERT文のキャッシュ・エントリE(1)が再利用可能とマークされていて、キャッシュ・エントリの数がすでにMAXOPENCURSORSに達しているとします。プログラムが新しい文を実行する場合、キャッシュ・エントリE(1)とそのプライベートSQL領域は新しい文に再度割り当てられることがあります。INSERT文を再実行するために、サーバーではそれを再度解析しなおして、別のキャッシュ・エントリを再度割り当てる必要があります。
再利用できるキャッシュ・エントリが見つからない場合、サーバーでは追加のキャッシュ・エントリが割り当てられます。たとえば、MAXOPENCURSORS=8で、8エントリすべてがアクティブな場合、9番目のエントリが作成されます。サーバーでは、空きメモリーがなくなるかOPEN_CURSORSで設定された上限に達するまで、必要に応じてキャッシュ・エントリの割当てが続行されます。この動的割当ては、処理オーバーヘッドを増大させます。
したがって、MAXOPENCURSORSの値を小さく設定すると、メモリーの節約にはなりますが、新しいキャッシュ・エントリの動的割当ておよび割当て解除に資源を消耗する場合があります。MAXOPENCURSORSの値を大きく設定すると、実行は確実に速くなりますが、より大きなメモリーを使用することになります。
実行回数の少ないSQL文とそのプライベートSQL領域間のリンクは、一時的なものにした方がよい場合もあります。
HOLD_CURSOR=NO(デフォルト値)と指定した場合は、サーバーによりそのSQL文が実行され、カーソルがクローズされた後に、プリコンパイラによりこのカーソルとカーソル・キャッシュ間のリンクが再利用可能としてマークされます。このリンクは、それが示すカーソル・キャッシュ・エントリが別のSQL文に必要になると、すぐに再利用されます。これにより、プライベートSQL領域に割り当てられたメモリーが解放され、解析ロックが解除されます。ただし、PREPAREしたカーソルは実行状態のままにする必要があるため、HOLD_CURSOR=NOと指定した場合でもそのリンクは維持されます。
RELEASE_CURSOR=YESと指定した場合は、サーバーによりそのSQL文が実行され、カーソルがクローズされた後に、プライベートSQL領域が自動的に解放され、解析した文は失われます。メモリーの節約のためにMAXOPENCURSORSを低い値に設定しているような場合には、この指定が必要です。
DML文がデータ定義文より前にあり、どちらの文も同じ表を参照する場合には、DML文にRELEASE_CURSOR=YESを指定してください。これにより、データ操作文で取得される解析ロックと、データ定義文で要求される排他ロックとの間の競合が回避できます。
RELEASE_CURSOR=YESを指定した場合は、プライベートSQL領域とキャッシュ・エントリ間のリンクはただちに削除され、このプライベートSQL領域は解放されます。HOLD_CURSOR=YESを指定している場合でも、RELEASE_CURSOR=YESによりHOLD_CURSOR=YESが上書きされるため、サーバーではSQL文を実行する前に、プライベートSQL領域にメモリーを再度割り当てて、このSQL文を再解析する必要があります。
ただし、RELEASE_CURSOR=YESを指定した場合、サーバーではSQL文とPL/SQLブロックの解析済の表現が共有SQLキャッシュに保存されるため、それ以上の再解析処理は不要になることがあります。カーソルをクローズしても、解析された表現はキャッシュの内容が書き換えられるまで効力を持ちます。
プライベートSQL領域には文の実行に必要なすべての情報が格納されているため、頻繁に実行されるSQL文とそのプライベートSQL領域のリンクを維持する必要があります。この情報へのアクセスを上手に管理すると、後続の文の実行速度をさらに向上させることができます。
HOLD_CURSOR=YESを指定した場合は、サーバーによりSQL文が実行された後に、カーソルとカーソル・キャッシュのリンクが維持されます。したがって、解析された文および割り当てられたメモリーが、利用可能なまま維持されます。これは、不必要な再解析を避けるためにアクティブにしておくSQL文で役に立ちます。
RELEASE_CURSOR=NO(デフォルト値)の場合、サーバーでSQL文を実行した後にキャッシュ・エントリとプライベートSQL領域間のリンクが維持され、オープンしたカーソルの数がMAXOPENCURSORSの値を超えないかぎり、そのリンクは再利用されません。これは、解析した文および割り当てたメモリーが使用可能な状態のままのため、頻繁に実行するSQL文の場合に有効です。
注意:
Oracle8iの以前のバージョンでは、SQL文の実行後にRELEASE_CURSOR=NOおよびHOLD_CURSOR=YESとなっていると、解析後の表現を継続して使用できました。ただし、Oracleの後続のバージョンでは、RELEASE_CURSOR=NOおよびHOLD_CURSOR=YESが指定されている場合、解析後の表現を使用できるのは共有SQLキャッシュの内容が期限切れになるまでの間です。通常、これは問題にはなりませんが、そのSQL文が再解析される前に参照オブジェクトの定義が変わると、結果が予期せぬものになる場合があります。
カーソルを管理するために、埋込みPL/SQLブロックはSQL文と同様に扱われます。埋込みPL/SQLブロックが実行されると、親カーソルがPL/SQLブロック全体に対応付けられ、埋込みPL/SQLブロック用にキャッシュ・エントリとPGAのプライベートSQL領域の間にリンクが作成されます。埋込みブロック内の各SQL文にも、PGAのプライベートSQL領域が必要なことに注意してください。これらのSQL文は、PL/SQLが管理する子カーソルを使用します。子カーソルの性質は、対応付けられた親カーソルによって決まります。つまり、子カーソルが使用するプライベートSQL領域は、親カーソルのプライベートSQL領域が解放された後解放されます。
図B-1に、HOLD_CURSORとRELEASE_CURSORの相互関係を示します。HOLD_CURSOR=NOを指定すると、RELEASE_CURSOR=NOはオーバーライドされ、RELEASE_CURSOR=YESを指定すると、HOLD_CURSOR=YESがオーバーライドされることに注意してください。
表B-1 HOLD_CURSORとRELEASE_CURSORの相互関係
HOLD_CURSOR | RELEASE_CURSOR | リンク |
---|---|---|
NO |
NO |
再利用可能としてマーク |
YES |
NO |
維持 |
NO |
YES |
ただちに削除 |
YES |
YES |
ただちに削除 |