Groovyでテーブルのデータを取得したい(4)
先日の日記で書いたGroovyスクリプトを実行してみたところ、少ない件数では問題ありませんが、
ちょっと多いデータだと全然スクリプトが帰ってきませんでした。
試しに19テーブル、合計約4万件のデータが抽出されるような状況で試したところ、
5分以上かかってもスクリプトが応答しなかったので、これは使い物にならないと思い改良することに。
テーブルのデータ件数は、以下のようになっています。
テーブル名 | 件数 | テーブル名 | 件数 |
---|---|---|---|
BIZ_AB_PROJECT | 5 | BIZ_AB_VALIDATE | 34 |
BIZ_AB_WIDGET | 36 | BIZ_AB_ENTITY | 46 |
BIZ_AB_BEHAVIOR | 48 | BIZ_AB_VALIDATE_RULE | 88 |
BIZ_AB_DATA_DOMAIN | 119 | BIZ_AB_LOGIC | 145 |
BIZ_AB_LAYOUT | 155 | BIZ_AB_PROP_MASTER | 156 |
BIZ_AB_DTO | 256 | BIZ_AB_LO_BEHAVIOR | 302 |
BIZ_AB_KEYBIND | 431 | BIZ_AB_L_BEHAVIOR | 566 |
BIZ_AB_ENTITY_FIELD | 663 | BIZ_AB_DTO_FIELD | 1947 |
BIZ_AB_L_WIDGET | 2116 | BIZ_AB_PROP | 14833 |
BIZ_AB_PROP_VALUE | 17406 |
先日のコードだと1つ1つの行列ごと、つまり1つのセルごとにEXCEL関数をコールしていることになるので、
上のデータで試すと約4万行×20列分コールされることになります。
r.each{ worksheet.Cells(rowNumber,colNumber++).value = it.value } // これが約80万回コールされる
これをせめて行単位にコールすることができれば、もう少し短縮できるのではないかと。80万→4万
これには、SafeArrayを使用することにします。
SafeArrayには、org.codehaus.groovy.scriptom.SafeArrayと com.jacob.com.SafeArrayがあるので注意してください。
使用するのは、org.codehaus.groovy.scriptom.SafeArrayです。
import java.text.SimpleDateFormat import org.codehaus.groovy.scriptom.ActiveXObject import org.codehaus.groovy.scriptom.SafeArray def sql = groovy.sql.Sql.newInstance('jdbc:oracle:thin:@localhost:1521:ORCL','userid','password','oracle.jdbc.driver.OracleDriver') def xlApp = new ActiveXObject('Excel.Application') def username = "INIT" def workbook = xlApp.workbooks.Add SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss") sql.eachRow("SELECT table_name FROM user_tables WHERE table_name LIKE 'BIZ_AB_%'"){ rs-> def rows = sql.rows("SELECT * FROM " + rs.table_name +" WHERE create_user_cd ='" + username + "'") if( rows.size() > 0 ){ def worksheet = workbook.worksheets.Add worksheet.name = rs.table_name long rowNumber = 1 for(r in rows){ SafeArray safeArray = null if(1==rowNumber){ worksheet.Range("A${rowNumber}:T${rowNumber}").Value = new SafeArray( r*.key.toArray() ) rowNumber++ } r.each{ if( it.value instanceof oracle.sql.TIMESTAMP ) it.value = sdf.format(it.value.dateValue()) } worksheet.Range("A${rowNumber}:T${rowNumber}").Value = new SafeArray( r*.value.toArray() ) rowNumber++ } } } xlApp.visible = true
依然として、対象とするテーブルがTIMESTAMP型を含んでいるため、変換のためにr.eachがありますが、
EXCEL関数はeachの外に出せます。
これで計測すると、約140秒で終了するようになりました。300秒かかっても応答がなかったころに比べれば
まだましですが、2分以上かかるのも考えものです。。。
EXCEL関数のworksheet.Rangeは範囲を指定できるので、SafeArrayに1行分のデータだけでなく、
すべてのデータを含めることができれば4万回→19回となり、だいぶ早くなるのではないかと思います。
ただ、SafeArrayのコンストラクタに複数行分を指定するようなものが見当たらないのですが。。。
もうすこし調べてみたいと思います。。