#43 さまざまなエクスポート方法の比較

結論
Accessからエクスポートできるファイル形式はいろいろあるが、その処理時間に関しては、今回のテストの範疇では次のような順位付けができる。
<速い>
  1. CSVファイル(Print #ステートメント)
  2. Excelワークシート
  3. HTMLファイル
  4. CSVファイル(DoCmdオブジェクト)
<遅い>

Accessでは、テーブルやクエリのレコードを簡単に外部のさまざまなファイル形式に出力することができます。メニューバーの操作で個々に出力することもできますし、マクロを使って自動化することもできます。さらにVBAを使えば、テーブルやクエリのレコードを1件ずつ読み込んで、フィールドごとに任意の値に加工しながら出力することも可能です。

しかし逆に言えば、さまざまな出力ファイル形式、さまざまな出力手段があるということは、その中のどのような方法がもっとも速くエクスポート処理できるのか気になるところです。そこで今回は、VBAを使っていくつかのファイル形式およびエクスポート命令を実行し、その処理時間の違いを調査してみます。


まず出力するファイル形式ですが、次の3種類を比較してみます。

  1. Excelワークシート
  2. CSV(カンマ区切り記号付きテキスト)ファイル
  3. HTMLファイル
また、VBAの命令として次の2種類を比較してみます。
  1. DoCmdオブジェクトのメソッドを使った方法
  2. Print #ステートメントを使った方法

DoCmdオブジェクトについては、ExcelワークシートはTransferSpreadsheetメソッド、他はTransferTextメソッドを使うことになります。また、Print #ステートメントを使ってのエクスポートについては、一般的にはCSVファイル出力が多いはずですので、CSVファイルへの出力のみをテストするものとします。つまり、全体として4通りのパターンでテスト・比較することになります。


テストに用いるプログラムコードですが、以下のようなものを用意しました。

Sub ExportTest()

  Dim dbs As Database
  Dim rst As Recordset
  Dim lngFileNum As Long
  Dim iintLoop As Integer

  ts_Watch "処理開始", True
  
  For iintLoop = 1 To 10
    'テストパターン1
    DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, _
                               "tbl受注", "受注.XLS"
    ts_Watch "XLSエクスポート"

    'テストパターン2
    DoCmd.TransferText acExportDelim, , "tbl受注", "受注.CSV"
    ts_Watch "CSVエクスポート(DoCmd)"

    'テストパターン3
    DoCmd.TransferText acExportHTML, , "tbl受注", "受注.HTM"
    ts_Watch "HTMLエクスポート"

    'テストパターン4
    lngFileNum = FreeFile()
    Open "受注.CSV" For Output As #lngFileNum
    Set dbs = CurrentDb
    Set rst = dbs.OpenRecordset("tbl受注")
    With rst
      Do Until .EOF
        Print #lngFileNum, !受注コード & "," & !商品コード & "," & _
                            !単価 & "," & !数量 & "," & !割引
        .MoveNext
      Loop
      .Close
    End With
    Close #lngFileNum
    ts_Watch "CSVエクスポート(Print #)"

  Next iintLoop

End Sub


DoCmdオブジェクトのメソッドを使った方法については、文法に沿ってメソッドや引数を指定して、1行の命令によってそれぞれのパターンを実行します。

またPrint #ステートメントを使った方法については、DAOを使ってテーブルを開き、1件ずつ読み込みながら、CSV形式になるようカンマを付けた文字列を組み立てて、それをテキストファイルに書き出しています。今回は特にフォーマットの変換などはなく、テーブルに保存されているデータをそのまま出力しています。

いずれの方法についても、ループで10回処理し、その合計時間等を確認してみます。なお、テーブル「tbl受注」には5万件のレコードが保存されています。この数字は、出力したファイルをExcelで読み込む際、その読み込み可能最大行数(約6万数千)を意識したものです。


そして、そのテスト結果は次のようなものになりました。いずれも単位は秒です。
合計 平均 最大 最小
Excelワークシート 20.98 2.10 3.03 1.03
CSVファイル(DoCmdオブジェクト) 44.06 4.41 4.52 3.44
HTMLファイル 37.27 3.73 3.75 3.67
CSVファイル(Print #ステートメント) 12.50 1.25 1.27 1.23
まずExcelワークシート形式にエクスポートする方法ですが、ワークシートの内部構造がどのようなものか分かりませんが、Accessのテーブルからそのような複雑そうな形式に変換するわけですから、その処理時間に時間を要するのではと推測していたのですが、実際にはDocmdオブジェクトの中では最も、しかもかなり速く処理されることが分かりました。単純な形式であるCSVファイルの半分以下の処理時間です。どうやら複雑そうに思えるExcelワークシートの構造も、Accessからみれば比較的近い形の構造となっているのかしれません。そして、AccessにとってはCSVというテキスト形式に変換することの方が苦手なのかもしれません(これはAccessというよりTransferTextメソッドの能力の問題かもしれませんが....)。

また、HTMLファイル形式への出力についても、Excelワークシートにはかなり劣るものの、CSVファイルより優っているというのも意外でした。HTML形式の場合、データを区切るためのHTMLタグが入るため、出力するデータ量そのものはCSVより多くなっています。単純に書き込み量だけみればCSVの方が速そうなのですが、結果はそうはなっていません。

一方、Print #ステートメントを使った方法は抜群の結果が出ているといえます。DocmdオブジェクトのTransferTextメソッドと同じ内容のファイルが作られているにもかかわらず、一方はExcelワークシートより倍以上遅く、一方は倍近く速いという意外な結果です。CSVファイル出力どうしを比較すると、DocmdオブジェクトとPrint #ステートメントとでは4倍近い開きがあります。この結果に対する理由は明確ではありませんが、マクロを使ったりDocmdの命令1行で済ませずに、細かいVBAのコードを数行書くことを惜しまなければ、それ相応のパフォーマンスが得られることは確かです。

Accessの場合、エクスポートできる外部ファイルの形式は他にもいろいろありますが、今回行ったテストの範疇では、次のような順位付けができると思います。
  1. CSVファイル(Print #ステートメント)←最速

  2. Excelワークシート

  3. HTMLファイル

  4. CSVファイル(DoCmdオブジェクト)←最遅
実際の場面では、"どうしてもXLSに出力したい"、"ブラウザ対応でHTML形式にしたい"、"フィールドを変換しながら出力したいのでPrint #ステートメントによるCSVしかない"というように、その方法が限定されるケースも少なくないと思います。しかし、非常に大量のデータを出力する場面において、もし、その出力方法は問わず、できるだけ速く処理させたいということであれば、この結果は非常に参考になるのではないでしょうか?。



さて、上記テストの結果、Print #ステートメントを使った方法は、プログラムのコードを書くのは少々長く面倒ですが、パフォーマンス面ではなかなか使えるということが分かりました。そこで、これを応用することでさらに速く処理できないかということで、次のようなコードを考えてみました。ここでのポイントは、外部のテキストファイルに実際にデータを書き出す命令、すなわちPrint #ステートメントの実行を1回だけに抑えている点です。テーブルからの読み込み処理のループ内では個々に書き出しは行わず、ひたすらstrOutPutという文字列変数にそのデータを付け加えていきます。そしてループを抜けたところで一気にそれを書き出そうというものです。

Sub CSVTest2()

  Dim dbs As Database
  Dim rst As Recordset
  Dim lngFileNum As Long
  Dim strOutPut As String

  lngFileNum = FreeFile()
  Open "受注.CSV" For Output As #lngFileNum
  Set dbs = CurrentDb
  Set rst = dbs.OpenRecordset("tbl受注")
  With rst
    strOutPut = ""
    Do Until .EOF
      strOutPut = strOutPut & !受注コード & "," & !商品コード & "," & _
                              !単価 & "," & !数量 & "," & !割引 & vbCrLf
      .MoveNext
    Loop
    .Close
  End With
  Print #lngFileNum, strOutPut
  Close #lngFileNum

End Sub


このプログラムの実行結果ですが、実際にやってみると、ループなしのたった1回の処理にもかかわらず、その終了を待ち切れずに途中で強制中断させてしまうほど時間がかかるということが分かりました。ファイルへの出力というと、ハードディスクを物理的に動かす書き出し処理に時間がかかるのではと思っていたのですが、実際には、長い文字列の演算を行う方がよほど時間を要するということのようです。「Print #ステートメントを使う際には短く刻んで出力していった方がよい」ということは明らかでした。
| Index | Prev | Next |

 

Copyright © T'sWare All rights reserved