Jの衝動書き日記

さらりーまんSEの日記でございます。

Oracleカーソルのメモ

 暇だったので、Oracleカーソルに関してちょっといじって理解した結果を書いておく。

カーソルとは何か?

アプリケーションが発行したSQLの問い合わせ結果を逐次受け取るための仕組みである。
具体的に言うと、Oracleのサーバ上に、SQLの実行結果を格納した領域があり、そこからちょっとづつ取り出していく仕組みである。
そのため、取り出し可能なデータは、SQL発行時のもののみである。カーソルからデータを取り出している最中に、データに追加・変更・削除があってもその影響は受けない。

カーソルからのデータ取り出しにおけるアクセスについて

アプリケーションより、カーソル経由でデータを取得する時は、1件づつ取得していくが、この1件に付き、データベースアクセスが1回発生しているわけではない。
  実際のOracleへのアクセスは一定数まとめて取得してキャッシュしている(Oracleのライブラリ部が行う)。
  取り出すデータがなくなった場合はORA-1403が返り、その状態でカーソルからデータを取得すると -1002のエラー応答となる。

プログラムにおけるカーソル利用について(Pro*C)

カーソルはCLOSEしない限り利用可能である。そのため、一定数データを取得したら応答を返し、再度取得する時は、次のデータから継続するといったことが出来る。
  カーソルの定義と、カーソルのOPEN、CLOSEは同一ソースファイル上である必要があるが、同一関数内である必要はない。
  また、Oracleから一定数まとめて取得する件数は、PREFETCHオプションで指定可能である。
  ソース上で指定する場合は、カーソルオープン前にEXEC ORACLE OPTION (PREFETCH=10);などとやる。

Javaの場合   

Java JDBC

カーソルの利用は、ResultSetの利用に相当する。ResultSetをcloseしない限りデータは継続して取得できるため、Pro*Cと同様に一定数データを取得したら応答として返す、といったことが出来る(while(resultset.next())とかやると、データ取得がすべて終わるまで制御は戻せない)。

一度に取得する件数はOracleStatement.setRowPrefetch で個別変更、OracleConnection.setDefaultRowPrefetchで接続毎の変更可能だが、Oracle固有処理となる。また、デフォルトでは10のため、特に変更する必要もない。
なお、Resultset, Statementをcloseしても特になにも送信はされず、 Connectionのclose時にOracleと通信するようだ。    

hibernate   

JDBCと異なり、一定数取得する毎に制御を戻し、その後再開といったことは出来ない。listメソッドを実行する度にSQLを発行しているためである。
    だが、 setFirstResultとsetMaxResultsで結果の範囲取得が可能である。setFirstResultを使用すると、自動生成するSQLにrownum取得のSQLが付き、それを元に範囲取得する。ただ、その度にSQLが実行される点には注意。バッチ処理には不向き。
   

iBatis

queryWithRowHandlerを用いることで、取得データ一件ずつの処理が可能。だが、一定数取得したら制御を戻し、再開というのはやはり出来ない。実行の度にSQLを発行する点はhibernateと一緒である。
queryForListでリストの範囲取得が可能だが、実際は該当箇所に行くまで取得したデータを読み飛ばしているだけのため、データ件数が多く、後ろに行くほど無駄な取得処理が発生する。