いつもどこかでデスマーチ♪

不定期に、私の日常を書き込みしていきます。

Excel POIで セル結合を使う(追加・削除)はやめた方が無難

2018年12月15日 20時55分39秒 | メモ
Excel POI Ver3.17

セル結合情報は「sheet.getMergedRegions()」で取得できます

セル結合の削除は「sheet.removeMergedRegion(index)」で出来ます。
(削除すると内部が減るので、再取得、またはIndexの大きいほうから消すこと)

セル結合の追加は「sheet.addMergedRegion(CellRangeAddress)」で出来ます。



普通に考えたら、削除時だけ注意すればそんなに難しくないのですが…
これがめちゃくちゃ遅い。超遅い。死ぬほど遅い…

約20000件のデータを下記例のフォーマットでエクセルに出力する時…1時間以上かかりました…


【No、発生日時、種類、対象、内容】の各入力欄をセル結合する人が多いのかなぁと思ってます。(私はそうでした)

その場合、POIで行う場合、下記ソースの様に「行追加→スタイルのコピー→罫線追加→値の追加」の順番になると思います

// 反映元行を取得
final Row srcRow = CellUtil.getRow(copySrcIndex, this.sheet);
// 反映先行を取得
final Row destRow = CellUtil.getRow(copyDestIndex, this.sheet);
final CellRangeAddress address = new CellRangeAddress(destRow.getRowNum(),
                                                      destRow.getRowNum(),
                                                      1,
                                                      2);
RegionUtil.setBorderTop(BorderStyle.THIN, address, this.sheet);
RegionUtil.setBorderRight(BorderStyle.THIN, address, this.sheet);
RegionUtil.setBorderBottom(BorderStyle.THIN, address, this.sheet);
RegionUtil.setBorderLeft(BorderStyle.THIN, address, this.sheet);
this.sheet.addMergedRegion(address);         // これが曲者…
final Cell noCell = CellUtil.getCell(destRow, 1);
final CellStyle noStyle = this.wb.createCellStyle();
noStyle.cloneStyleFrom(srcRow.getCell(1).getCellStyle());
noStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
noStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
noCell.setCellStyle(noStyle);
noCell.setCellValue(srcRow.getCell(1).getStringCellValue());



その場合、「this.sheet.addMergedRegion(address);」処理が遅いです。
例の画像の場合、1行に対して上記処理を5回行います。それが遅すぎるようです。
ちなみに、十分な行を先に作成しておいても、削除する場合は同じです。追加・削除はほぼ同じ時間が掛ります。
逆に言えば、想定行数が決まっている場合、そこからの差分のセル結合量が少なければ、問題無いと思います。



で、この遅さはいかんともしがたい!セル結合の処理を行わなければいいのだ!
どのように対策したか…「選択範囲内で中央」です。
セル結合の操作をしなければ良いので、「中央揃え」を「選択範囲内で中央」に変更します。
そうすることで、無事に中央揃えが出来ます。データは右に増える分は常に見えています。(右揃えはダメだった気が…)

というわけで、下記で実装しました。
スタイルが指定されている行のセル分ループし、コピー先のセルにスタイルをコピーします。
注意点は「選択範囲内で中央」で使用するセル全てに「HorizontalAlignment.CENTER_SELECTION」を設定することです

// 反映元行を取得
final Row srcRow = CellUtil.getRow(copySrcIndex, this.sheet);
// 反映先行を取得
final Row destRow = CellUtil.getRow(copyDestIndex, this.sheet);

for (int colIndex = srcRow.getFirstCellNum(); colIndex < srcRow.getLastCellNum(); colIndex++) {

    final CellStyle srcCellStyle = CellUtil.getCell(srcRow, colIndex).getCellStyle();
    if (colIndex < 19) {

        // 対象より左のセルは選択範囲の中央揃えとする
        srcCellStyle.setAlignment(HorizontalAlignment.CENTER_SELECTION);
    }
    CellUtil.getCell(destRow, colIndex).setCellStyle(srcCellStyle);
}



こうすることで、セル結合を行わずに、中央ぞろえ、罫線等のスタイルがコピーされます。(workbook.createCellStyle() を行っていない事に注意)
if分の部分は仕様に合わせて変更してあげてください。


こうすることで、
約20000件のデータを出力した場合…約4~5秒で終わりました!
驚異的スピードです


というわけで、ExcelのTemplateにデータ出力はいろんな場面で使用されると思います。
が、行数が未定のデータを「中央揃え」にする場合「選択範囲内で中央」を使用し、セル結合・解除を可能な限り処理しない様に工夫しましょう。

もちろん、セル結合をPOIを使って増減させなければ速度に影響はありません。
また、2万行をセル結合の状態で作るのと、今回の方法で作るのと、実際にWindowsのExcel で開く時間も段違いです。
私のPCでエクセルを実行した場合…
セル結合したエクセルファイルは 5秒前後かかりました。
セル結合無しのエクセルファイルは 1秒未満でした。
(POIで開くときも遅いです)

というわけで、実際のExcelでもセル結合はやめた方が良いですね。

いや…早くなって良かった…
しかし、セル結合しない中央揃えって、嫌われるんだよな…orz


蛇足ですが、workbook.createCellStyle() は6000個ぐらいで例外が発生しました。同じスタイルを使う場合は作らずにコピーで良いです


Tomcat:8.0.53
Java:1.8.0_191
POI:3.17


検索用:Apache POI Excel 遅い 使えない 速度 改善 早く セル結合 セル結合解除 スタイル 中央 揃え 選択範囲内で中央

コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« Extjs の hbox vbox の設定… | トップ | PGAdminV4 の バージョン4 .1... »

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。

メモ」カテゴリの最新記事