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のコンストラクタに複数行分を指定するようなものが見当たらないのですが。。。


もうすこし調べてみたいと思います。。